Skip to Content
Course content

311: TypedDict for Typed Dictionaries

Click on the "Edit" button in the top corner of the screen to edit your slide content.

You've probably spent plenty of time passing dictionaries around your code. They're the Swiss Army knife of Python—perfect for when you need to bundle some data together without the overhead of defining a full class. But as your project grows, a plain dictionary becomes a liability. When you're looking at a function signature that says data: dict, you have no idea what's actually inside that dictionary without hunting through the code to find where it was instantiated.

The "Anything Goes" Dictionary

Let's look at how we usually handle this when we're moving too fast. Imagine we're building a system to handle user sessions. We might write something like this:

def process_session(session: dict):
    # I'm just guessing these keys exist
    username = session["username"]
    user_id = session["user_id"]
    print(f"Processing {username} ({user_id})")

On the surface, this is fine. But here is where it breaks: your IDE has no clue what session contains. You get no autocomplete for the keys, and if you accidentally type session["user_ID"] (uppercase D), Python won't complain until the code actually crashes at runtime with a KeyError. If you try to fix this by using Dict[str, Any], you've essentially just told the type checker, "Trust me, it's a dictionary, but don't ask me what's in it." That's not really helping us.

Adding a Schema with TypedDict

This is where TypedDict comes in. It allows you to define a "schema" for your dictionary. It doesn't change how the dictionary behaves at runtime—it's still just a regular Python dict—but it gives your static analysis tools (like Mypy or Pyright) the blueprint they need to protect you.

from typing import TypedDict

class UserSession(TypedDict):
    username: str
    user_id: int
    is_admin: bool

def process_session(session: UserSession):
    # Now my IDE knows exactly what keys are available
    username = session["username"] 
    print(f"Processing {username}")

The difference here is subtle but massive. If I try to assign a string to user_id or access a key called email that isn't in the UserSession definition, my editor will highlight it in red immediately. I don't have to run the code to find the bug.

The Cost of Rigidity

Now, you might wonder why we wouldn't just use a dataclass or a NamedTuple. The trade-off is about intent and compatibility. If you're working with JSON APIs, your data arrives as a dictionary. Converting every single API response into a class instance takes time and adds boilerplate. TypedDict lets you keep the lightweight nature of a dictionary while gaining the safety of a class.

One thing to keep in mind: TypedDict is strictly for type checking. If you pass a dictionary that is missing a required key into process_session, Python will not stop you at runtime. It's a contract for the developer, not a validation layer for the data. If you need the code to actually crash the moment a key is missing during execution, you'll still want to look at something like Pydantic.




📋 Practical Task

Refactoring the Movie Metadata Pipeline

You have a function that processes movie data coming from a legacy JSON API. Currently, it uses a generic dictionary, which is causing bugs because some developers are using "year" while others are using "release_year".

Your Task:

  • Define a TypedDict called MovieMetadata that requires three keys: title (str), release_year (int), and rating (float).
  • Update the analyze_movie function signature to use MovieMetadata instead of dict.
  • Create a movie dictionary that deliberately misses one of the required keys or uses the wrong type, and observe how your type checker (or IDE) flags the error.
from typing import TypedDict

# TODO: Define MovieMetadata TypedDict here

def analyze_movie(movie: dict): # TODO: Update this type hint
    print(f"Analyzing {movie['title']} from {movie['release_year']}")

# TODO: Create a movie instance and pass it to the function
movie_data = {
    "title": "Inception",
    "release_year": 2010,
    "rating": 8.8
}

analyze_movie(movie_data)
Rating
0 0

There are no comments for now.

to be the first to leave a comment.