What is PEP 484?
PEP 484: Type Hints is the 2014 proposal that gave Python optional static types. Accepted for Python 3.5, it defines what annotations like name: str mean, introduces the typing module, and establishes the gradual-typing model that every Python type checker implements.
The PEP was co-authored by Guido van Rossum and Jukka Lehtosalo, who created mypy. Type hints grew out of mypy’s annotation syntax, then became a language standard that other tools could build on.
Separate type checking from runtime behavior
Python stores annotations but never enforces them. This program runs without complaint:
def greet(name: str) -> str:
return f"hello {name}"
greet(42) # runs fine and returns "hello 42"The annotations only matter to tools that read them: a type checker like mypy, Pyright, or ty flags greet(42) as an error without running the code, and editors use the same information for completion and inline documentation.
That separation is the core design decision of PEP 484. The interpreter’s behavior never changes; correctness checking happens in a separate tool, at whatever strictness you choose.
Adopt annotations gradually
Gradual typing lets annotated and unannotated code coexist. Operations on Any escape static type checking, but missing annotations do not make every value Any: checkers can infer types from assignments and expressions.
Pyright checks unannotated function bodies by default and infers their return types; mypy skips fully unannotated bodies by default. See how Python type checkers compare for the checking policies.
Add annotations one module, function, or parameter at a time to make intended types explicit. How to gradually adopt type checking in an existing Python project walks through that process.
Use modern annotation syntax
Use the annotation syntax supported by the project’s minimum Python version:
| PEP 484 original | Modern equivalent | Since |
|---|---|---|
typing.List[int] |
list[int] (PEP 585, builtin generics) |
3.9 |
Union[int, str], Optional[int] |
int | str, int | None (PEP 604, union operator) |
3.10 |
T = TypeVar("T") plus Generic[T] |
class Box[T]: (PEP 695, type parameter syntax) |
3.12 |
| Quoted forward references | Unquoted, evaluated lazily (PEP 649, deferred annotations) | 3.14 |
The gradual-typing semantics underneath are unchanged. A checker written against PEP 484’s model handles all of these spellings.
Choose a type checker
Four major type checkers implement the PEP 484 model today: mypy (the original), Pyright (which powers VS Code’s Pylance), ty, and Pyrefly. How do Python type checkers compare? covers choosing between them.
Ruff is not a type checker (Does Ruff support type checking? explains the distinction), but its UP-prefixed rules automatically rewrite PEP 484-era syntax to the modern equivalents in the table.
The typing_extensions package backports new typing constructs to older Python versions, which is how libraries use features like ParamSpec (PEP 612, typed decorators) while still supporting Pythons that predate them.