TypedDict lets Python type checkers understand dictionaries with a known set of keys. It is useful when data naturally arrives as a dictionary but the project still needs clear field names, value types, and editor feedback.
Quick Answer
Define a TypedDict class for the dictionary shape, use ordinary annotations for required keys, and use NotRequired or Required when optionality differs by field. Remember that TypedDict guides static analysis; it does not validate an incoming dictionary at runtime.

The official typing.TypedDict documentation covers class and functional syntax, totality, inheritance, and introspection. PEP 589 defines the original TypedDict design, while PEP 655 describes required and potentially-missing keys.
Define A Basic TypedDict
By default, every declared key is required according to the type checker.
from typing import TypedDict
class User(TypedDict):
name: str
age: int
user: User = {'name': 'Ada', 'age': 36}
The annotation does not construct a special dictionary object. The assignment is still a normal dictionary, but tools such as mypy or pyright can check whether its keys and values match the declared shape.
Model An Optional Key With NotRequired
Use NotRequired when one field can be omitted while the rest of the record remains total.
from typing import NotRequired, TypedDict
class User(TypedDict):
name: str
email: NotRequired[str]
user: User = {'name': 'Ada'}
When reading an optional key, handle its absence just as you would with any ordinary dictionary. A type checker may require a membership check or a safe get() call before code assumes the value exists.

Use total=False For Partial Records
total=False makes all fields potentially absent. Add Required to the fields that must remain present.
from typing import NotRequired, Required, TypedDict
class Patch(TypedDict, total=False):
name: Required[str]
email: NotRequired[str]
This is useful for patch payloads, configuration updates, and partially assembled records. Choose the form that makes the external data contract easiest to read.
TypedDict In Function Signatures
TypedDict is particularly useful at function boundaries because the expected record shape appears next to the function that consumes it.
from typing import TypedDict
class Point(TypedDict):
x: int
y: int
def distance_label(point: Point) -> str:
return f"{point['x']},{point['y']}"
Keep the runtime behavior in mind: indexing a missing key still raises KeyError. Static analysis can warn about many mistakes before execution, but it does not replace a runtime check when data comes from a request, file, or third-party service.

Runtime Limits And Validation
At runtime, isinstance(value, dict) only proves that the object is a dictionary. It does not prove that required keys exist or that the values have the annotated types.
from typing import get_type_hints
print(get_type_hints(User))
print(isinstance({'name': 'Ada'}, dict))
For untrusted input, validate the data at the boundary with explicit checks or a validation library, then pass the validated result through code annotated with TypedDict. This separates runtime safety from static documentation.
Inheritance And Compatibility
TypedDict classes can inherit from other TypedDict classes when a specialized record extends a base shape. For Python versions before NotRequired and Required were available in the standard library, use the compatible definitions from typing_extensions.
Functional syntax is also available when a key is not a valid Python identifier, for example a key containing a hyphen. Use it sparingly; the class form is usually easier for readers to maintain.
Common TypedDict Mistakes
- Expecting TypedDict to validate JSON or API input automatically.
- Using
total=Falsewhen only one field is optional. - Indexing an optional key without checking whether it exists.
- Assuming a TypedDict value has a different runtime class from dict.
- Forgetting to run the project’s type checker in CI.
The practical rule is to use TypedDict when a dictionary is the right runtime structure and the project needs a precise static contract. Add explicit runtime validation wherever the data crosses a trust boundary.

Required Keys And Dictionary Operations
Static type checkers track the declared key set, but ordinary dictionary operations still follow normal Python rules. user['name'] can raise KeyError if unvalidated data is missing a key. user.get('email') is more appropriate when a field is optional and the absence itself is expected.
When updating a TypedDict, keep the update compatible with the declared value type. A type checker may reject a misspelled key or incompatible value even though Python’s runtime dictionary would accept it. That early warning is one of the main reasons to annotate the shape.
TypedDict And External Data
JSON, form data, and database rows usually arrive as untyped dictionaries. Parse and validate those values at the boundary, then annotate the validated result. Do not use a type comment as evidence that the data has been checked.
A small explicit validator can be enough for a stable internal payload: check required keys, test their value types, handle optional fields, and return the dictionary only after those checks succeed. Larger projects may prefer a dedicated validation or schema library.

Generic And Inherited Shapes
TypedDict can be combined with generic type parameters and inherited record shapes in supported Python versions. Use inheritance when the relationship is real, such as a base event with common keys and a specialized event that adds more fields.
A deep hierarchy can make a payload harder to understand. Keep the public data contract close to the code that consumes it, and use a short name that communicates whether the record is an input, output, patch, or stored model.
Run A Type Checker In CI
TypedDict provides value when a type checker actually runs. Configure mypy, pyright, or the project’s chosen tool, then add the command to local development and continuous integration. A type annotation that is never checked is documentation only.
Keep the Python version configured consistently across the editor, checker, and deployment. Differences in support for Required, NotRequired, and newer generic syntax can otherwise create confusing editor-versus-CI results.
TypedDict Compared With Other Models
Use TypedDict when the runtime value should remain a dictionary and keys are the important contract. A dataclass is often clearer when behavior, defaults, and attribute access matter. A validation model is a better fit when parsing untrusted input and producing runtime errors is the primary job.
For typed mapping design, compare TypedDict with OrderedDict and dictionary size checks. Read python ordereddict and python dictionary size for the related workflow.
Frequently Asked Questions
What is TypedDict in Python?
TypedDict describes the expected keys and value types of a dictionary for static type checkers. The value used at runtime is still an ordinary dict.
Are TypedDict keys required by default?
Yes. A TypedDict is total by default, so declared keys are expected to be present. Use NotRequired for individual optional keys or total=False for a partial dictionary.
Does TypedDict validate data at runtime?
No. TypedDict is a static typing construct and does not automatically reject a missing key or wrong value at runtime. Validate external data separately.
When should I use NotRequired?
Use NotRequired when a field may be absent while the other fields remain required. Use Required inside a total=False TypedDict when one field must still be present.