Python Type Hints¶
Status: 🟢 Active | Owner: Python Guild
Requirement¶
All production code must have complete type annotations. mypy in strict mode is enforced in CI.
Basic Annotations¶
from __future__ import annotations # enables forward references everywhere
def place_order(customer_id: str, items: list[OrderItem]) -> OrderConfirmation:
...
def find_order(order_id: str) -> Order | None:
...
Use from __future__ import annotations at the top of every module to enable PEP 563 deferred evaluation. This allows forward references without quotes and is especially useful for models that reference themselves.
Modern Type Syntax (Python 3.10+)¶
Prefer the modern X | Y union syntax over Optional[X] and Union[X, Y]:
# ✅ Modern (Python 3.10+)
def find(id: str) -> Order | None: ...
def process(value: int | str) -> str: ...
# ❌ Verbose legacy forms — avoid in new code
from typing import Optional, Union
def find(id: str) -> Optional[Order]: ...
def process(value: Union[int, str]) -> str: ...
TypedDict¶
Use TypedDict for dictionaries with known shapes, such as config dicts or legacy API responses:
Prefer Pydantic BaseModel over TypedDict for anything that needs validation.
Protocol¶
Use Protocol to define structural interfaces (duck-typing with type checking):
from typing import Protocol
class OrderRepository(Protocol):
async def save(self, order: Order) -> None: ...
async def find_by_id(self, id: str) -> Order | None: ...
This decouples the interface from the implementation without inheritance.
Generics¶
from typing import Generic, TypeVar
T = TypeVar("T")
class Page(Generic[T]):
items: list[T]
total: int
page: int
size: int
Common mypy Errors and Fixes¶
| Error | Fix |
|---|---|
error: Function is missing a return type annotation | Add -> ReturnType: |
error: Call to untyped function | Add type hints to the called function or install stubs |
error: Incompatible return value type | Fix return type or narrow the type before returning |
error: Item "None" of "X \| None" has no attribute "y" | Check for None before accessing: if obj is not None: |
Suppression Policy¶
Use # type: ignore[error-code] sparingly, with a comment explaining why:
# type: ignore without an error code is not permitted — it silences all errors on that line.
References¶
Last reviewed: 2025-Q4 | Owner: Python Guild