Annotations and type checkers

typing · Essentials · 9 min

Type hints catch whole classes of bugs before running code and make editors far more helpful.

Python annotations attach types to names:

def greet(name: str, times: int = 1) -> str: ...
count: int = 0

The interpreter does not enforce them. At runtime greet(42) still runs; the hints are stored in __annotations__ and otherwise ignored. Their value comes from tools:

  • Static type checkers (mypy, pyright, also built into VS Code's Pylance and PyCharm) read your code and report mismatches without running it.
  • Editors use hints for autocompletion and safe refactoring.
  • Some libraries (FastAPI, Pydantic, dataclasses) read hints at runtime to validate or generate code.

Checkers infer most local variable types (x = 3 is an int), so annotate function signatures and class attributes, and let inference do the rest. A function with no annotations is usually treated as untyped and not checked by mypy's default settings, so start adding types at function boundaries.

Run a checker as part of your workflow: mypy src/ or pyright. Start lenient, then tighten with options like mypy's --strict as coverage grows.

reveal_type(expr) (in typing since 3.11) asks the checker to print the type it inferred; at runtime it prints the actual type.

Example

from typing import reveal_type

def area(width: float, height: float) -> float:
    return width * height

total: float = area(2, 3.5)      # ints are accepted where float is expected
print(total)                     # 7.0
print(area.__annotations__)
# {'width': <class 'float'>, 'height': <class 'float'>, 'return': <class 'float'>}

print(area("ab", 2))             # runs! prints abab
# mypy/pyright: error: Argument 1 to "area" has incompatible type "str"

reveal_type(total)               # checker: float; runtime prints the actual type

Remember

  • Hints describe types; Python does not enforce them at runtime
  • Static checkers (mypy, pyright) verify them before code runs
  • Annotate signatures; let inference handle locals

Watch out for

  • Expecting a TypeError at runtime from a wrong argument type; only a checker reports it.

Check yourself

  1. def f(x: int) -> int: return x * 2, then f('a') is executed. What happens?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/typing.html