Python TypedDict: Type Checking Dictionary Schemas
Python's typing.TypedDict enables static type checkers
to validate dictionaries with fixed sets of keys and specific value
types, offering a schema-enforcement mechanism without altering runtime
behavior. While standard Python dictionaries are dynamic and
traditionally typed homogenously using annotations like
dict[str, Any], TypedDict provides
fine-grained type safety for structured, heterogeneous dictionary data
such as JSON payloads and database records.
The Problem with Traditional Dictionary Typing
In standard Python typing, the generic type dict[K, V]
assumes that all keys share the type K and all values share
the type V. In real-world applications, structured data
rarely follows this pattern. For example, an API response often has
string keys mapped to varied types:
user = {
"id": 101,
"name": "Alice",
"is_active": True
}Annotating this object as dict[str, Any] loses all
structural safety: static analyzers such as Mypy or Pyright cannot
verify that "id" is an integer, nor can they flag missing
keys or spelling mistakes such as accessing
user["is_activ"].
How
TypedDict Solves Schema Validation
TypedDict allows developers to define a type schema
using class syntax or functional syntax. Static type checkers treat
instances of this type as plain dictionaries at runtime, but enforce key
presence and value types during static analysis.
from typing import TypedDict
class UserProfile(TypedDict):
id: int
name: str
is_active: bool
# Correct usage
user: UserProfile = {
"id": 101,
"name": "Alice",
"is_active": True
}
# Type-checker errors:
# 1. Missing required key 'is_active'
# 2. Incompatible value type for 'id' (str instead of int)
invalid_user: UserProfile = {
"id": "101",
"name": "Bob"
}Key Capabilities
- Static Key and Type Enforcement: Type checkers verify that all declared keys exist and their corresponding values match the declared types. Accessing an undefined key will trigger an error during static analysis.
- Handling Optional Keys: By default, all declared
keys are required (
total=True). Settingtotal=Falsemakes all keys optional. For granular control in Python 3.11+,typing.Requiredandtyping.NotRequiredspecify key requirements on an individual basis:
from typing import TypedDict, NotRequired
class Configuration(TypedDict):
host: str
port: int
timeout: NotRequired[float] # Optional key- Zero Runtime Overhead:
TypedDictcreates standard dictionaries. It does not introduce runtime overhead, instantiate custom classes, or perform runtime validation likepydanticordataclasses. At runtime,isinstance(user, UserProfile)raises aTypeErrorbecauseTypedDictis purely an analysis construct.
Practical Role in Python Codebases
TypedDict bridges the gap between dynamic dictionaries
and strict models. It is ideal for codebases consuming external APIs,
parsing configuration files, or interacting with legacy systems where
converting raw dictionaries into class instances would add unnecessary
deserialization cost or break backwards compatibility.