TypedDict
standardintermediateTypedDict declares the exact keys a dict should have and the type of each value — for when a dict is really "a record with a fixed shape," not an arbitrary mapping. The result is still a plain dict at runtime.
Think of it as
A TypedDict is a printed form with labeled fields, not a class instance — {"title": ..., "year": ...} is still just a dict, but the TypedDict tells a type checker which keys must be there and what type belongs in each, the way a form tells you which boxes to fill and how.
What we're doing: Declare a TypedDict for a movie record and confirm the resulting value is still a plain dict at runtime.
- 3
- class Movie(TypedDict): declares the required shape — every Movie must have exactly these keys.
- 7
- m is built as a plain dict literal — TypedDict only affects how a type checker reads it, not the runtime type.
{'title': 'Arrival', 'year': 2016} <class 'dict'>Why this works: type(m) is dict, not Movie — TypedDict is purely a type-checking construct layered on top of the ordinary dict type. Movie(...) is never actually called as a constructor the way a class normally is; m is a plain dict literal that a type checker treats as matching (or not matching) the Movie shape.
Expecting TypedDict to validate required keys at runtime, like a dataclass would
Wrong
Better
What you see: A dict missing a required TypedDict key is built and used with no exception at all — the gap only shows up if a separate type checker run catches it.
Why: TypedDict, like every other typing construct, is read only by a static type checker. Python's dict literal syntax has no way to enforce "these keys are required" at runtime — that guarantee exists only in whatever tool reads the annotation, or in code you write yourself.
Remember: TypedDict declares a dict's expected keys and value types for a type checker. The result is a plain dict at runtime — nothing is validated when it runs.
See also: type aliases and annotated · generic collection annotations

