typing
Python’s typing module provides runtime support for type hints and a vocabulary of more advanced hints that plain classes like int and str can’t express. Type hints make your programs more readable, safer to refactor, and help static type checkers catch errors before runtime.
This module is a cornerstone of Python’s gradual typing system, allowing for enhanced code clarity and static analysis.
Here’s a quick example:
>>> from typing import TypedDict
>>> class Person(TypedDict):
... name: str
... age: int
...
>>> def print_people(people: list[Person]) -> None:
... for person in people:
... print(f"{person['name']} is {person['age']} years old")
...
>>> print_people(
... [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]
... )
Alice is 30 years old
Bob is 25 years old
Hints like these are what a static type checker reads. Pick an argument below to see what mypy reports about the same call to greet():
The checker flags the call site before you run anything, naming both the type it got and the type the hint promised.
Key Features
- Supports type hinting for variables, functions, and classes
- Allows for custom type creation with
NewType, and withTypeVarwhen you need to support Python 3.11 and earlier, since Python 3.12 and later prefer the nativeclass C[T]:type-parameter syntax - Supports defining reusable type aliases for better code readability
- Enables complex type definitions
- Supports defining structural subtyping interfaces with protocols
Frequently Used Classes and Functions
| Object | Type | Description |
|---|---|---|
typing.NamedTuple |
Class | Creates tuple-like classes with named fields |
typing.TypedDict |
Class | Creates dictionary-like classes with a fixed set of keys |
typing.Callable |
Special form | Deprecated alias to collections.abc.Callable that represents a callable with a specified signature |
typing.Any |
Class | Represents a type that can be any value |
typing.TypeVar |
Class | Creates a generic type variable |
typing.NewType |
Class | Creates a distinct type based on an existing one |
typing.Protocol |
Class | Defines a structural subtyping interface that classes can implement |
typing.Self |
Special form | Represents the enclosing class in method annotations |
typing.override |
Decorator | Marks a method as intentionally overriding a superclass method |
Examples
Define a class using NamedTuple and a function that takes it as an argument:
>>> from typing import NamedTuple
>>> class User(NamedTuple):
... name: str
... age: int
...
>>> def greet(user: User) -> str:
... return f"Hello {user.name}, age {user.age}"
...
>>> greet(User("Alice", 30))
'Hello Alice, age 30'
Define a type alias to simplify complex type annotations:
>>> type UserData = dict[str, str | int]
>>> def format_user(user: UserData) -> str:
... return f"{user['name']} is {user['age']} years old"
...
>>> format_user({"name": "Charlie", "age": 40})
'Charlie is 40 years old'
Create a protocol and implement it in a class:
>>> from typing import Protocol
>>> class Greeter(Protocol):
... def greet(self) -> str:
... ...
...
>>> class FriendlyGreeter:
... def greet(self) -> str:
... return "Hello, friend!"
...
>>> def welcome(greeter: Greeter) -> None:
... print(greeter.greet())
...
>>> welcome(FriendlyGreeter())
Hello, friend!
Common Use Cases
- Specifying expected types for function parameters and return values
- Defining complex data structures with precise type information
- Enhancing code readability and maintainability
- Assisting static type checkers like
mypyin identifying potential errors
Real-World Example
Imagine you’re building an order processing system where you want to apply discounts to products. Using the typing module, you can create distinct type aliases, type variables, and callables to make your code safer and more expressive.
Since Python 3.12, you can declare generics inline with PEP 695 type-parameter syntax, writing class Stack[T]: or def first[T](items: list[T]) -> T: without explicitly creating a TypeVar. Creating a TypeVar explicitly remains valid and is mainly useful for compatibility with Python 3.11 and earlier.
The NewType form shown below serves a separate purpose: defining distinct nominal types, which the new syntax doesn’t provide.
>>> from collections.abc import Callable
>>> from typing import NewType
>>> OrderId = NewType("OrderId", int)
>>> def apply_discount(
... price: float, discount_fn: Callable[[float], float]
... ) -> float:
... return discount_fn(price)
...
>>> def process_order(
... order_id: OrderId,
... price: float,
... discount_fn: Callable[[float], float]
... ) -> str:
... final_price = apply_discount(price, discount_fn)
... return f"Order No. {order_id}: final price ${final_price:.2f}"
...
>>> ten_percent_off = lambda p: p * 0.9
>>> print(process_order(OrderId(101), 50.0, ten_percent_off))
Order No. 101: final price $45.00
This example shows how to use NewType to create a type for order IDs and Callable to define a flexible function signature for discounts. These type hints make the order processing code more robust and maintainable.
Related Resources
Tutorial
Python Type Checking (Guide)
In this guide, you'll look at Python type checking. Traditionally, types have been handled by the Python interpreter in a flexible but implicit way. Recent versions of Python allow you to specify explicit type hints that can be used by different tools to help you develop your code more efficiently.
For additional information on related topics, take a look at the following resources:
- Python Protocols: Leveraging Structural Subtyping (Tutorial)
- Python Type Checking (Course)
- Python Type Checking (Quiz)
- Exploring Protocols in Python (Course)
- Python Protocols: Leveraging Structural Subtyping (Quiz)
By Leodanis Pozo Ramos • Updated July 30, 2026