Python typing.Annotated
typing.Annotated
is a powerful Python feature that allows you to attach metadata to type hints, enabling richer type information that can be leveraged by libraries, frameworks, and your own code.
What is Annotated?
Annotated[T, metadata]
lets you declare a type T
while attaching additional information (metadata) about how that type should be interpreted or processed. The metadata can be any arbitrary Python object or objects.
from typing import Annotated
# Basic usage
name: Annotated[str, "first letter is capital"]
At runtime, the metadata can be accessed via .__metadata__
on the annotation.
Why Use Annotated?
Key Benefits
- Rich type information: Add validation rules, documentation, or processing instructions
- Framework integration: Many modern Python libraries leverage annotated types
- Backward compatibility: Code that doesn't understand the metadata ignores it safely
- Reduced boilerplate: Eliminate repetitive validation code
Real-World Use Cases
1. Data Validation with Pydantic
Pydantic uses Annotated
to define field constraints directly in type annotations:
from typing import Annotated
from pydantic import BaseModel, Field
# Without Annotated (more verbose)
class OldModel(BaseModel):
x: int
# Requires separate validator function
# With Annotated (concise and declarative)
class NewModel(BaseModel):
x: Annotated[int, Field(lt=20, gt=2)]
2. API Parameter Validation with FastAPI
FastAPI leverages Annotated
for request parameter validation:
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
def read_items(q: Annotated[str, Query(max_length=50)]):
return {"q": q}
This tells FastAPI that parameter q
should be a string with maximum length of 50 characters.
3. GraphQL Schema Definition with Strawberry
Strawberry GraphQL uses Annotated
for handling circular dependencies and lazy type references:
from typing import Annotated
import strawberry
@strawberry.type
class User:
name: str
friends: Annotated[list["User"], strawberry.lazy(".__main__")]
4. CLI Applications with Typer
Typer uses Annotated
for defining CLI parameters with rich metadata:
from typing import Annotated
import typer
def main(name: Annotated[str, typer.Argument(help="Your name")]):
print(f"Hello {name}")
5. State Management with LangGraph
LangGraph uses Annotated
to define how state should be updated:
from typing import Annotated, TypedDict
def reducer(a: list, b: float) -> list:
return a + [b]
class State(TypedDict):
x: Annotated[list, reducer]
How to Access Metadata
You can extract metadata from annotated types using get_type_hints()
with include_extras=True
:
from typing import Annotated, get_type_hints
class MyClass:
value: Annotated[int, "Important value", {"min": 0, "max": 100}]
hints = get_type_hints(MyClass, include_extras=True)
metadata = hints["value"].__metadata__
print(metadata) # ("Important value", {"min": 0, "max": 100})
Best Practices
When to Use Annotated
- When working with libraries that support it (Pydantic, FastAPI, etc.)
- To reduce boilerplate validation code
- When you need to attach processing instructions to types
- For creating self-documenting code with embedded constraints
Considerations
- Metadata is ignored by Python's native type checking
- Not all tools and IDEs fully support
Annotated
yet - Use judiciously - excessive metadata can make code harder to read
Advanced Usage
You can attach multiple metadata items to a single type:
from typing import Annotated
from pydantic import Field
import some_library
ComplexType = Annotated[
str,
Field(min_length=1, max_length=100),
some_library.ValidationRule("email"),
"User email address",
{"category": "personal_info"}
]
Conclusion
typing.Annotated
transforms Python's type system from simple type declarations to rich, descriptive annotations that can carry validation rules, documentation, and processing instructions. While the feature itself doesn't enforce anything, it enables powerful patterns when used with supporting libraries like Pydantic, FastAPI, and others.
As the Python ecosystem continues to embrace type annotations, Annotated
is becoming increasingly valuable for creating more expressive, maintainable, and self-documenting code.