TypedDict for dict-shaped data

typing · Practical · 11 min

APIs and config files produce dicts; TypedDict lets you check key names and value types without converting to classes.

dict[str, Any] says nothing about which keys exist. A TypedDict declares them:

class Movie(TypedDict):
    title: str
    year: int

At runtime a Movie is a plain dict; Movie(title="x", year=1) just builds one. The checker, however, knows m["title"] is a str, flags typos like m["titel"], and rejects missing or extra keys in literals.

Key presence:

  • All keys are required by default. total=False makes all of them optional.
  • NotRequired[T] marks individual keys optional; Required[T] does the reverse inside a total=False class.
  • Use m.get("key") for optional keys; the result type includes None.

`ReadOnly[T]` (3.13, PEP 705) marks keys that must not be assigned through this type, which lets a function accepting a "read-only view" take TypedDicts with compatible but narrower value types.

TypedDicts support inheritance and generics. The alternative functional syntax, Movie = TypedDict("Movie", {"title": str}), works for keys that aren't valid identifiers.

Choose TypedDict for data that stays a dict (JSON, kwargs); choose a dataclass when you want an object with methods.

Example

from typing import NotRequired, ReadOnly, TypedDict

class User(TypedDict):
    id: ReadOnly[int]
    name: str
    email: NotRequired[str]

def greeting(u: User) -> str:
    domain = u.get("email", "no email")       # optional key: use get()
    return f"{u['name']} <{domain}>"

u: User = {"id": 1, "name": "Ada"}
print(greeting(u), type(u).__name__)          # Ada <no email> dict
print(User.__required_keys__, User.__optional_keys__)
# frozenset({'id', 'name'}) frozenset({'email'})  (set order may vary)
# u["nmae"]          -> checker error: TypedDict "User" has no key "nmae"
# u["id"] = 2        -> checker error: ReadOnly key "id" cannot be assigned
# bad: User = {"name": "x"}   -> checker error: missing key "id"

Remember

  • TypedDict types dicts with known keys; at runtime it's a plain dict
  • NotRequired / total=False mark optional keys
  • ReadOnly (3.13) forbids assignment to a key

Watch out for

  • Expecting runtime validation; TypedDict(...) accepts any values, only the checker complains.

Check yourself

  1. What is type(User(id=1, name='x')) at runtime, where User is a TypedDict?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/typing.html#typing.TypedDict