Learning Python - Type Checking & Contracting
Series/Learn Python/Episode 17
Episode 17 of 23

Learning Python - Type Checking & Contracting

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.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

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.

Type Checking Basics

Installing and Running mypy

mypy is the most popular type checker:

Install mypy
pip install mypy
PythonKode beranotasi untuk diperiksa
def 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:

Menjalankan mypy
mypy typed.py

mypy 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 and TypedDict

Annotated for Additional Metadata

Annotated adds metadata alongside a type:

PythonMenggunakan Annotated
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 for Structured Dicts

TypedDict gives a type to a dict with fixed keys:

PythonMenggunakan TypedDict
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 and Structural Typing

Understanding Protocol

Protocol enables structural typing — objects are accepted based on their shape, not their inheritance:

PythonMenggunakan Protocol
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 and Callable

Forwarding Functions with Types

ParamSpec captures a function's parameters so they can be forwarded:

PythonMenggunakan ParamSpec
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.

mypy and pyright in Practice

pyright: An Alternative from Microsoft

pyright is a fast type checker integrated into editors:

Install pyright
npm install -g pyright
Menjalankan pyright
pyright typed.py

pyright 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.

Benefits for Large Codebases

Documentation and Maintenance

Typing documents code automatically:

  • Function signatures become clear contracts.
  • IDEs show types on hover.
  • New developers understand the structure faster.
  • Type bugs are caught in CI, not in production.

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.

Closing

Key takeaways:

  • mypy and pyright check types without running the program.
  • Annotated adds metadata alongside a base type.
  • TypedDict types a dict with fixed keys.
  • Protocol enables structural typing based on object shape.
  • ParamSpec and Callable preserve the types of forwarded functions.
  • Typing makes refactoring safe and maintenance easier.

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!

Learning Python - Type Checking & Contracting | Learn Python