Free ToolBy GitIntel

Python Type Hints: The Practical Guide for Production Code

Beyond the basics — how to type async code, generics, Pydantic models, and the mypy vs pyright decision.

GitIntel tracks AI-generated code across your entire git history — giving every tool on this page the attribution layer that standard dev tooling misses.

Try GitIntel free

Python type hints went from optional to expected in professional Python codebases between 2020 and 2026. PEP 484 introduced hints in Python 3.5; by 3.12, the typing ecosystem matured enough that the major frameworks (FastAPI, Pydantic, SQLAlchemy 2.0) are built around type hints as a first-class feature, not an afterthought.

The mypy vs pyright choice is the first decision. mypy (maintained by the Python community, used by Dropbox, Stripe, Facebook) is the reference implementation. pyright (Microsoft, powers Pylance in VS Code) is faster and stricter. In practice: pyright catches more errors and runs significantly faster on large codebases (5-10x faster than mypy at scale). For new projects in 2026, pyright/Pylance via the `pylance` VS Code extension is the default. basedpyright is a community fork with even stricter defaults.

Pydantic v2 is how most production Python code does runtime type validation. Define a model as a Python class with type annotations, and Pydantic validates inputs, coerces types, and generates JSON schemas automatically. The v2 rewrite (2023) moved the core to Rust — validation is 5-50x faster than v1. FastAPI uses Pydantic internally for request/response validation, so you get type-safe API development without additional tooling.

Typing async code: use `Coroutine[ReturnType, ...]` for coroutines, `AsyncIterator[T]` for async generators, and `Awaitable[T]` for anything that can be awaited. The `asyncio.gather` return type needs proper annotation — `asyncio.gather(*coros)` returns `tuple[T1, T2, ...]` in Python 3.11+ with the `Unpack` TypeVarTuple. Use `from __future__ import annotations` at the top of files with forward references to avoid evaluation order issues.

Generics with TypeVar: `T = TypeVar('T')` for basic generics. Python 3.12 introduced PEP 695 syntax: `def first[T](items: list[T]) -> T` — cleaner and more readable than the older TypeVar approach. For protocol-based structural subtyping (duck typing with type safety), use `Protocol` instead of ABCs when you don't own the classes you're typing.

The patterns that catch the most bugs in practice: annotate return types (not just parameters), use `Optional[T]` (or `T | None` in 3.10+) explicitly instead of implicit None returns, and run mypy/pyright in CI with `--strict` from day one rather than adding it later to a large codebase.

Frequently Asked Questions

Do Python type hints affect runtime performance?

Type hints are not enforced at runtime by default — they're purely for static analysis tools and documentation. Accessing `__annotations__` is negligible overhead. The one exception is Pydantic and similar libraries that use type hints for runtime validation, which does add CPU overhead — but this is intentional and the cost is accounted for in Pydantic's performance benchmarks.

What is the difference between mypy and pyright?

Both are Python static type checkers. pyright is generally stricter (catches more issues), significantly faster on large codebases, and better integrated with VS Code via Pylance. mypy has broader configurability and is considered the reference standard for PEP compliance. For new projects, pyright is the practical default. For teams already on mypy, the migration cost usually isn't worth the switch unless performance or strictness is a pain point.

Should I add type hints to existing Python code?

Yes, incrementally. Add `# type: ignore` to suppress errors on existing code rather than trying to type everything at once. Use `mypy --ignore-missing-imports --allow-untyped-defs` to start permissively. Type the public API of each module first (function signatures), then internal functions. The highest-value targets: code that changes frequently, code called from many places, and any function that accepts or returns complex nested structures.

Start Using GitIntel Free

Open source. No account required. Works on any git repository.