Data JSON dan hasil query database lazim hadir sebagai dictionary dengan bentuk tetap: key yang sama, tipe value yang sudah diketahui. Anotasi dict[str, int] tidak bisa menggambarkan kontrak semacam ini — anotasi itu hanya bicara “semua value bertipe int”, padahal bentuk nyatanya lebih kaya: nama selalu string, harga selalu integer, tag berupa list. TypedDict dari module typing mengisi kekosongan ini: sebuah definisi bernama untuk bentuk dictionary, lengkap dengan tipe per key.

Mendefinisikan dan Menggunakan TypedDict #

Definisi TypedDict ditulis seperti class, tetapi setiap atributnya adalah anotasi key:

python
from typing import TypedDict

class MenuToko(TypedDict):
    nama: str
    harga: int

menu: MenuToko = {"nama": "Kopi Susu", "harga": 18000}
print(menu["nama"], menu["harga"])
print(isinstance(menu, dict))

Output yang Dihasilkan:

text
Kopi Susu 18000
True

Mari kita telaah baris-barisnya:

  1. Baris 3-5 (class MenuToko): nama dan harga bukan atribut object — keduanya adalah kontrak key. Di runtime, MenuToko berubah menjadi dictionary biasa.
  2. Baris 7 (menu: MenuToko = {...}): isi dictionary diperiksa terhadap kontrak — key harus lengkap, dan setiap value harus cocok tipenya.
  3. Baris 9 (isinstance(menu, dict)): mengonfirmasi hakikat runtime-nya — True, karena menu memang dictionary; TypedDict tidak membuat class baru yang berjalan.

Checker memperlakukan menu["nama"] bertipe str dan menu["harga"] bertipe int — melanggar salah satunya langsung tertangkap. Simpan sebagai menu.py:

python
from typing import TypedDict

class MenuToko(TypedDict):
    nama: str
    harga: int

menu: MenuToko = {"nama": "Kopi Susu", "harga": "delapan ribu"}
bash
python3 -m mypy menu.py

Output yang Dihasilkan:

text
menu.py:7: error: Incompatible types (expression has type "str", TypedDict item "harga" has type "int")  [typeddict-item]
Found 1 error in 1 file (checked 1 source file)

total=False untuk Key Opsional #

Secara bawaan (total=True), semua key wajib ada. Untuk key yang boleh absen, definisikan TypedDict dengan total=False:

python
from typing import TypedDict

class ProfilPengguna(TypedDict, total=False):
    nama: str
    kota: str

profil: ProfilPengguna = {"nama": "Budi"}
print(profil)

profil["kota"] = "Bandung"
print(profil)

Output yang Dihasilkan:

text
{'nama': 'Budi'}
{'nama': 'Budi', 'kota': 'Bandung'}

Karena kota opsional, dictionary yang hanya berisi nama langsung sah. Akses key opsional tetap perlu hati-hati: menurut checker, profil["kota"] berstatus str (checker mempercayai key tersebut ada), sedangkan profil.get("kota") berstatus str | None — jujur terhadap kemungkinan key belum diisi. Di runtime, profil["kota"] menaikkan KeyError bila key-nya memang belum pernah diisi, jadi gunakan .get() ketika absennya key wajar terjadi.

TypedDict Bersarang #

Value dari sebuah key bisa berupa TypedDict lain — cara alami memodelkan JSON bertingkat:

python
from typing import TypedDict

class Alamat(TypedDict):
    kota: str
    kode_pos: str

class Pengguna(TypedDict):
    nama: str
    alamat: Alamat

pengguna: Pengguna = {
    "nama": "Siti",
    "alamat": {"kota": "Bandung", "kode_pos": "40115"},
}
print(pengguna["alamat"]["kota"])

Output yang Dihasilkan:

text
Bandung

Akses bertingkat pengguna["alamat"]["kota"] ikut terperiksa: checker tahu pengguna["alamat"] bertipe Alamat, sehingga "kota" sah dan bertipe str, sedangkan key yang tidak dikenal akan ditolak.

TypedDict atau Dataclass? #

Keduanya memodelkan struktur data bernama, tetapi hakikat runtime-nya berbeda total:

AspekTypedDictDataclass
Wujud runtimedictionary biasaobject dengan atribut
Cara aksesmenu["harga"]produk.harga
Konversi dari/ke JSONlangsung (json.dumps)perlu asdict()
Method dan perilakutidak ada (murni data)bisa punya method
Pemeriksaan runtimetidak adatidak ada (sama-sama statis)

Aturan praktisnya: jika data keluar-masuk dunia luar sebagai dictionary — response API, baris csv.DictReader, hasil json.loads — TypedDict membiarkan data tetap dictionary tanpa lapisan konversi. Jika data punya perilaku atau akan diproses sebagai object, dataclass lebih tepat; kita telah membahasnya pada Chapter Dataclasses di Section Advanced Data Modeling.