Skip to main content

TypedDict, Literal, TypeGuard

advanced20 min readLesson 119 of 169

Type real-world data shapes: payloads, enums-as-literals, narrowing predicates.

TypedDict, Literal, TypeGuard โ€” typing data at the edges

TypedDict types dictionary-shaped data โ€” JSON, API payloads, config โ€” where keys are known and values vary per key:

from typing import TypedDict

class Movie(TypedDict):
    title: str
    year: int
    tags: list[str]

movie: Movie = {"title": "Coco", "year": 2017, "tags": ["animation"]}

At runtime it is a plain dict (no validation โ€” that is pydantic's job); the value is for the checker and for total=True/False key requirements.

Literal narrows a value to exact literals โ€” the type-safe replacement for magic strings:

from typing import Literal, TypeAlias
Mode: TypeAlias = Literal["r", "w", "a"]

def open_db(mode: Mode) -> None: ...

TypeGuard documents narrowing functions โ€” the predicate returns True and the checker then treats the argument as the guarded type:

from typing import TypeGuard

def is_str_list(xs: list[object]) -> TypeGuard[list[str]]:
    return all(isinstance(x, str) for x in xs)

Design guidance: type the edges of the system precisely (API responses, config, queue messages) and let internal code flow from those anchors. cast() exists for the rare moment you know more than the checker โ€” treat every cast as a comment that can lie, and prefer a TypeGuard or assert isinstance which actually checks.

Now practice

Project: Typed API ModelA validated order record: TypedDict shape, Literal status set, TypeGuard predicate.1 challenge ยท ยท ~25 min