How to Use TypedDict for Data Validation in Python

Define a TypedDict schema and validate raw dictionary input with type hints for safer, more readable data handling.

Medium Python 3.11+ Aug 9, 2026 Testing & modern typing 14 views 0 copies

Python code

38 lines
Python 3.11+
from typing import Any, Dict, List, Optional, Union, TypedDict, Literal

class Product(TypedDict):
    product_id: int
    name: str
    price: Union[int, float]
    in_stock: bool
    tags: Optional[List[str]]

def validate_product(data: Dict[str, Any]) -> Product:
    product_id: int = int(data["product_id"])
    name: str = str(data["name"])
    price: Union[int, float] = data["price"]
    in_stock: bool = bool(data["in_stock"])
    tags: Optional[List[str]] = data.get("tags")
    return {
        "product_id": product_id,
        "name": name,
        "price": price,
        "in_stock": in_stock,
        "tags": tags,
    }

def main() -> None:
    raw_data: Dict[str, Any] = {
        "product_id": 101,
        "name": "Fidget Spinner",
        "price": 4.99,
        "in_stock": True,
        "tags": ["toy", "fun"],
    }
    product: Product = validate_product(raw_data)
    print(f"Validated product: {product}")
    print(f"Name: {product['name']}, Price: {product['price']}")
    print(f"In stock: {product['in_stock']}, Tags: {product['tags']}")

if __name__ == "__main__":
    main()

Output

stdout
Validated product: {'product_id': 101, 'name': 'Fidget Spinner', 'price': 4.99, 'in_stock': True, 'tags': ['toy', 'fun']}
Name: Fidget Spinner, Price: 4.99
In stock: True, Tags: ['toy', 'fun']

How it works

TypedDict lets you define the exact shape and types of a dictionary at the type level, without runtime overhead. validate_product accepts a generic Dict[str, Any] and returns a Product, giving static checkers like mypy the ability to catch shape mismatches before they reach production. The explicit casts (int(), str(), bool()) handle raw values that come from JSON or an API where types may be loose. Using Optional[List[str]] for tags makes it clear that missing values are allowed. This pattern works well as a lightweight alternative to Pydantic when you prefer clever typing over third-party dependencies.

Common mistakes

  • Using TypedDict as a runtime validator — it only helps static type checkers, not runtime checks.
  • Forgetting to import `TypedDict` from `typing` (Python < 3.8) instead of the `typing_extensions` backport.
  • Redefining the TypedDict class inside a function, which breaks type checking in some tools.
  • Expecting `get()` with a default to preserve the declared Optional type — the default may need casing or a cast.

Variations

  1. Use `NotRequired` (from `typing_extensions` or Python 3.11+) to mark keys as optional rather than wrapping them in `Optional`.
  2. Return `TypedDict` directly from a cast of the raw data instead of rebuilding the dictionary manually.

Real-world use cases

  • Validating and typing API response dictionaries before passing them into business logic or an ORM.
  • Defining the shape of configuration files loaded at startup so misconfigured keys are caught by static analysis.
  • Modeling record shapes in data pipelines where each row is a dictionary that must respect a contract.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Testing & modern typing

Related tutorials and quizzes for this topic.