This episode covers static typing with the typing module: Annotated, TypedDict, Protocol, and ParamSpec, plus the mypy and pyright type checkers. You'll also understand the benefits of typing for maintenance, refactoring, and large codebases.

Python is a dynamically typed language, but that's no reason not to use types. Episode 17 teaches modern static typing with the typing module and type checkers like mypy and pyright.
Typing isn't just decoration — it's a contract that machines can check, catching bugs before runtime and documenting code automatically. We'll dissect Annotated, TypedDict, Protocol, and ParamSpec, then look at their benefits in large codebases.
mypy is the most popular type checker:
pip install mypydef tambah(a: int, b: int) -> int:
return a + b
hasil = tambah(1, 2)
print(hasil)def tambah(a: int, b: int) -> int: declares the parameter and return types. Save it as typed.py, then check it:
mypy typed.pymypy typed.py checks the annotations and finds type violations. If there's code calling tambah("a", 1), mypy reports it without running the program. A type checker catches bugs at development time, not at runtime.
Annotated adds metadata alongside a type:
from typing import Annotated
from pydantic import Field
usia = Annotated[int, Field(ge=0, le=150)]
def cek_usia(nilai: usia) -> str:
if nilai < 18:
return "remaja"
return "dewasa"
print(cek_usia(20))Annotated[int, Field(ge=0, le=150)] combines the int type with validation metadata. FastAPI and pydantic read this metadata for validation and automatic documentation. Annotated makes a type carry more information.
TypedDict gives a type to a dict with fixed keys:
from typing import TypedDict
class Pengguna(TypedDict):
nama: str
usia: int
aktif: bool
def tampilkan(data: Pengguna) -> str:
return f"{data['nama']} berusia {data['usia']}"
print(tampilkan({"nama": "Arman", "usia": 30, "aktif": True}))class Pengguna(TypedDict): defines a dict schema with a type per key. mypy checks that every key exists and has the right type. TypedDict brings type safety to JSON-like data without turning it into a class.
Protocol enables structural typing — objects are accepted based on their shape, not their inheritance:
from typing import Protocol
class BisaTerbang(Protocol):
def terbang(self) -> str: ...
class Burung:
def terbang(self) -> str:
return "Burung terbang"
class Pesawat:
def terbang(self) -> str:
return "Pesawat terbang"
def terangkan(objek: BisaTerbang) -> str:
return objek.terbang()
print(terangkan(Burung()))
print(terangkan(Pesawat()))class BisaTerbang(Protocol): defines a contract as a terbang method. Both Burung and Pesawat satisfy the contract even though they don't inherit from each other. Protocol gives duck typing flexibility with static type checking.
ParamSpec captures a function's parameters so they can be forwarded:
from typing import Callable, ParamSpec, TypeVar
P = ParamSpec("P")
T = TypeVar("T")
def ulangi_dua_kali(fungsi: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
hasil_pertama = fungsi(*args, **kwargs)
return fungsi(*args, **kwargs)
print(ulangi_dua_kali(lambda x: x + 1, 5))ParamSpec("P") captures the parameters of a forwarded function. Callable[P, T] declares the called function, and P.args plus P.kwargs forward them. mypy checks that re-calls have consistent types — important for decorators and wrappers.
pyright is a fast type checker integrated into editors:
npm install -g pyrightpyright typed.pypyright typed.py checks types quickly. Unlike mypy, which runs as a CLI, pyright is the engine behind the VS Code Python extension — providing real-time type checking as you type. Both can be used together; many teams use mypy in CI and pyright in the editor.
Typing documents code automatically:
For large teams, a typed codebase is easier to maintain because the contracts between modules are made explicit. The investment in writing annotations pays off many times over in the long run.
Key takeaways:
In the next episode, episode 18, we'll cover testing at scale — unit testing with pytest, test fixtures, parametrization, mocking, and property-based testing with hypothesis, plus integration and end-to-end test strategies. Your code will be proven correct with automated testing!