Skip to content

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.

python
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:

python
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:

python
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:

python
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:

python
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:

python
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:

python
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:

python
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.