Skip to content

Diagrams: Type Hints Explained

Back to concept


Type Annotation Hierarchy

Python's type system forms a hierarchy from simple types to complex generic types.

flowchart TD
    subgraph BASIC ["Basic Types"]
        INT["int"]
        STR["str"]
        FLOAT["float"]
        BOOL["bool"]
    end

    subgraph CONTAINER ["Container Types"]
        LIST["list[int]"]
        DICT["dict[str, int]"]
        SET["set[str]"]
        TUPLE["tuple[int, str]"]
    end

    subgraph SPECIAL ["Special Types"]
        OPT["Optional[str]<br/>= str | None"]
        UNION["Union[int, str]<br/>= int | str"]
        ANY2["Any<br/>(escape hatch)"]
    end

    subgraph ADVANCED ["Advanced Types"]
        CALLABLE["Callable[[int], str]"]
        TYPEVAR["TypeVar('T')"]
        GENERIC["Generic[T]"]
    end

    BASIC --> CONTAINER
    CONTAINER --> SPECIAL
    SPECIAL --> ADVANCED

    style BASIC fill:#4a9eff,stroke:#2670c2,color:#fff
    style CONTAINER fill:#51cf66,stroke:#27ae60,color:#fff
    style SPECIAL fill:#ffd43b,stroke:#f59f00,color:#000
    style ADVANCED fill:#cc5de8,stroke:#9c36b5,color:#fff

How Type Checking Works

Type hints do not run at runtime. A separate tool (mypy, pyright) reads them and reports errors.

flowchart LR
    subgraph WRITE ["You write"]
        CODE["def greet(name: str) -> str:<br/>    return 'Hello, ' + name"]
    end

    subgraph TOOLS ["Tools that read type hints"]
        MYPY["mypy<br/>Static checker"]
        PYRIGHT["pyright<br/>Static checker"]
        IDE["VS Code / PyCharm<br/>Autocomplete + warnings"]
    end

    subgraph RUNTIME ["At runtime"]
        PYTHON["Python ignores<br/>type hints completely"]
        RUNS["Code runs normally<br/>no speed cost"]
        PYTHON --> RUNS
    end

    WRITE --> TOOLS
    WRITE --> RUNTIME

    style WRITE fill:#4a9eff,stroke:#2670c2,color:#fff
    style TOOLS fill:#51cf66,stroke:#27ae60,color:#fff
    style RUNTIME fill:#ffd43b,stroke:#f59f00,color:#000

Optional and Union: Handling Multiple Types

Optional[X] means "X or None". Union[X, Y] means "X or Y". Modern Python uses | syntax.

flowchart TD
    subgraph OPT ["Optional[str]"]
        O1["Value can be str"]
        O2["Value can be None"]
        O3["Same as: str | None"]
        O1 ~~~ O2 ~~~ O3
    end

    subgraph UNI ["Union[int, str]"]
        U1["Value can be int"]
        U2["Value can be str"]
        U3["Same as: int | str"]
        U1 ~~~ U2 ~~~ U3
    end

    subgraph EXAMPLE ["Practical example"]
        E1["def find_user(id: int) -> Optional[User]:"]
        E2["    if found: return User(...)"]
        E3["    if not found: return None"]
        E1 ~~~ E2 ~~~ E3
    end

    OPT --> EXAMPLE
    UNI --> EXAMPLE

    style OPT fill:#4a9eff,stroke:#2670c2,color:#fff
    style UNI fill:#ff922b,stroke:#e8590c,color:#fff
    style EXAMPLE fill:#51cf66,stroke:#27ae60,color:#fff

Generics: Making Reusable Typed Code

Generics let you write functions and classes that work with any type while keeping type safety.

flowchart TD
    subgraph WITHOUT ["Without generics"]
        W1["def first(items: list) -> Any"]
        W2["Type checker loses<br/>track of the type"]
        W1 --> W2
    end

    subgraph WITH ["With generics"]
        G1["T = TypeVar('T')"]
        G2["def first(items: list[T]) -> T"]
        G3["first([1, 2, 3]) → int"]
        G4["first(['a', 'b']) → str"]
        G1 --> G2
        G2 --> G3
        G2 --> G4
    end

    style WITHOUT fill:#ff6b6b,stroke:#c0392b,color:#fff
    style WITH fill:#51cf66,stroke:#27ae60,color:#fff