Skip to content

← Interfaces with Protocols and ABCs step 10 of 26

Easy Primitives

Describe a Dictionary's Keys with TypedDict

A web server keeps its settings in a plain dictionary. Every config has a host and a port, and some also name a log_file. Give that dictionary a type, so mypy catches a misspelled key before the server starts.

A TypedDict lists a dictionary's keys and the type of each value. Here is one for a user record:

from typing import NotRequired, TypedDict


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


ada: User = {"name": "Ada"}
ada["email"] = "ada@example.com"

At runtime ada is a plain dict. The class is for the type checker. mypy now rejects a key that User does not list, a value of the wrong type, and a missing name. NotRequired marks a key that may be missing. It does not allow None as a value.

What mypy reports

Submit the starter as it is, and mypy reports three errors:

L18:39 [typeddict-item]
TypedDict "ServerConfig" has no key "prot"

L21:1 [empty-body]
Missing return statement

L30:16 [typeddict-unknown-key]
TypedDict "ServerConfig" has no key "log_file"

Every test fails too, because config['prot'] raises KeyError.

What to fix

  • In address, fix the misspelled key. address returns "host:port", such as "localhost:8000".
  • Add log_file to ServerConfig. Its value is a str, and a config may leave it out. If you declare it as a plain str, mypy reports Missing key "log_file" in solve, which builds some configs without it.
  • Write log_target. It returns the config's log_file, or "stderr" when the config has no log_file key. mypy does not warn about config["log_file"] on a key that may be missing, but that lookup raises KeyError when the key is not there.

Keep solve as it is. It builds the config, leaves log_file out when the test passes None, and returns what address and log_target give.

Edge cases

  • A log_file of "" is still a value. Return "", not "stderr". Only a missing key gives "stderr".
  • A port of 0 still appears in the address, as in "0.0.0.0:0".