Skip to content

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.

# CI check
mypy src/ --strict

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:

from typing import TypedDict

class OrderSummary(TypedDict):
    id: str
    total: float
    status: str

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:

result = external_lib.call()  # type: ignore[no-untyped-call]  # no stubs available

# type: ignore without an error code is not permitted — it silences all errors on that line.

References


Last reviewed: 2025-Q4  |  Owner: Python Guild