Type annotations in Python are a way to add explicit type information to variables, function parameters, and return values using a syntax introduced in PEP 484. They are used to improve code readability, enable better IDE support, and allow static type checking with tools like mypy, Pyright, or PyCharm.
Syntax and Basic Usage
Function parameters and return types:
def greet(name: str) -> str: return f"Hello, {name}!"Here,
name: strindicates the expected input type, and-> strspecifies the return type.Variable annotations:
count: int = 0 names: list[str] = ["Alice", "Bob"]
Key Features
No runtime enforcement: Type annotations are ignored by the Python interpreter. They do not raise errors if incorrect types are used at runtime.
Static analysis: Tools like mypy use annotations to catch type-related bugs before execution.
Improved developer experience: IDEs use type hints for auto-completion, refactoring, and error detection.
Advanced Type Annotations
Union types: Use
|(Python 3.10+) orUnion(earlier versions):def process(data: int | str) -> bool: return isinstance(data, int)Optional types: Use
X | NoneorOptional[X]:def get_user(id: int) -> str | None: # returns None if user not found passGenerics and TypeVar:
from typing import TypeVar, Generic T = TypeVar('T') class Box(Generic[T]): def __init__(self, item: T): self.item = itemTyped dictionaries:
from typing import TypedDict class Person(TypedDict): name: str age: int
Benefits
Better code documentation
Fewer runtime errors via static analysis
Improved collaboration and code maintainability
Enhanced tooling support (e.g., AI-assisted code completion)
For more details, refer to the official typing documentation and PEP 484.
How many Python core developers use type annotations?
python - How to properly function annotate / type hint a list of strings - Stack Overflow
python - How to use typing.Annotated - Stack Overflow
Type annotations in the stdlib - Core Development - Discussions on Python.org
Videos
Starting with Python 3.9, you can use list[str] as a type annotation, which doesn't require importing anything, as documented in PEP 585.
Python 3.5 standardizes the way function annotations are used for type hinting, as documented in PEP 484. To annotate a list of strings, you use List[str], where List is imported from the typing module. You can also use Sequence[str] if your function accepts any list-like sequence, or Iterable[str] for any iterable.
Python 3.4 and earlier doesn't specify a format for its function annotations, it merely provides a mechanism that allows you to use any expression as the annotation. How the annotations are interpreted is up to you and the libraries you use.
In Python 3.9+, list (with a lowercase l) can be used in type annotations and your code should work as is. On older versions of Python you need to import typing.List and use it instead
from typing import List
to_addresses: List[str]
Note the capital L.
You might want to consider something more specific, e.g.
import typing
Address = typing.NewType("Address")
See NewType docs
The static type checker will treat the new type as if it were a subclass of the original type
Annotated in python allows developers to declare the type of a reference and provide additional information related to it.
name: Annotated[str, "first letter is capital"]
This tells that name is of type str and that name[0] is a capital letter.
On its own Annotated does not do anything other than assigning extra information (metadata) to a reference. It is up to another code, which can be a library, framework or your own code, to interpret the metadata and make use of it.
For example, FastAPI uses Annotated for data validation:
def read_items(q: Annotated[str, Query(max_length=50)])
Here the parameter q is of type str with a maximum length of 50. This information was communicated to FastAPI (or any other underlying library) using the Annotated keyword.
Simply, it is a way of saying that there is metadata x for the type T:
Annotated[T, x]
use of which entirely depends on the library you are using (or your code)
Ultimately, the responsibility of how to interpret the annotations (if at all) is the responsibility of the tool or library encountering the Annotated type.
You can use it as a lazy type checking as in strawberry, since it can be safely ignored as explained above:
If a library (or tool) encounters a typehint Annotated[T, x] and has no special logic for metadata x, it should ignore it and simply treat the type as T.
Or you can use it for safer typing as in FastAPI