Sepanjang Section ini kita berkali-kali menulis “checker membaca kode tanpa menjalankannya”. Pernyataan tersebut layak dibedah dengan serius, karena dari situlah segala kekuatan dan segala batasan static type checking berasal: apa yang bisa diketahui sebelum eksekusi, apa yang baru terungkap saat eksekusi, dan bagaimana sebaiknya kita mengadopsinya pada project nyata. Pemahaman ini juga mencegah ekspektasi keliru — checker bukan alat pembasmi bug, melainkan alat pembasmi kelas bug tertentu.

Arti Kata Statis #

Static type checker adalah pembaca kode yang sangat teliti: mypy atau Pyright mengurai setiap berkas, membangun peta semua assignment dan pemanggilan, lalu mencari kontradiksi anotasi — semuanya tanpa mengeksekusi satu baris pun program. Konsekuensinya dua arah. Ke arah baik: pemeriksaan berjalan lengkap atas seluruh codebase, termasuk cabang if yang hari ini belum pernah dijalankan. Ke arah terbatas: checker tidak pernah tahu nilai apa yang sesungguhnya mengalir — variabel harga bisa saja berisi 0 atau -5000, dan itu bukan urusan checker.

Kontrasnya, uji runtime — menjalankan program atau pytest — melakukan kebalikannya: nilai-nilai nyata dievaluasi, tetapi hanya jalur yang benar-benar dilalui yang terperiksa.

Apa yang Ditangkap Checker #

Kelas galat yang menjadi makanan pokok checker berkaitan dengan bentuk program. Simpan sebagai produk.py:

python
class Produk:
    def __init__(self, nama: str) -> None:
        self.nama = nama

produk = Produk("Kopi")
print(produk.harga)
bash
python3 -m mypy produk.py

Output yang Dihasilkan:

text
produk.py:6: error: "Produk" has no attribute "harga"  [attr-defined]
Found 1 error in 1 file (checked 1 source file)

Atribut harga memang tidak pernah didefinisikan — di runtime, baris ini pasti menaikkan AttributeError. Checker menangkapnya lebih awal tanpa menjalankan program. Daftar tangkapan tipikal sejenis:

  • attribute yang tidak ada (produk.harga di atas) dan salah ketik nama method
  • argument bertipe salah pada function beranotasi (sapa(42) dari Chapter Pengenalan Type Hints)
  • None yang tidak ditangani pada nilai balik X | None
  • assignment yang mengganti tipe variabel secara tidak konsisten

Apa yang Hanya Muncul Saat Runtime #

Nilai bukan wilayah checker. Simpan sebagai pembagian.py:

python
def bagi(a: int, b: int) -> int:
    return a // b

print(bagi(10, 2))
print(bagi(10, 0))

Checker memberi nilai bersih:

bash
python3 -m mypy pembagian.py

Output yang Dihasilkan:

text
Success: no issues found in 1 source file

Namun saat program benar-benar dijalankan:

bash
python3 pembagian.py

Output yang Dihasilkan:

text
5
Traceback (most recent call last):
  File "pembagian.py", line 5, in <module>
    print(bagi(10, 0))
          ^^^^^^^^^^^
  File "pembagian.py", line 2, in bagi
    return a // b
           ~~^^~~
ZeroDivisionError: integer division or modulo by zero

Bagi checker, a // b dengan dua integer adalah operasi yang sah secara tipe — bahwa b berisi nol adalah fakta nilai yang baru ada saat pemanggilan berlangsung. Demikian pula file yang hilang, response API yang berubah, dan input pengguna yang aneh: semua adalah urusan runtime. Inilah alasan static checking tidak menggantikan testing — keduanya memburu hewan yang berbeda.

Batas Soundness #

Sebuah checker disebut sound bila tidak pernah meloloskan kode yang salah. Checker Python tidak sepenuhnya sound, dan kadang suara laporan “Success” lebih optimistis dari kenyataan. Simpan sebagai kamus.py:

python
def ambil_nilai(data: dict[str, int]) -> int:
    return data["kunci"]  # checker percaya kunci pasti ada

data_kosong: dict[str, int] = {}
print(ambil_nilai(data_kosong))
bash
python3 -m mypy kamus.py

Output yang Dihasilkan:

text
Success: no issues found in 1 source file

Sementara itu, python3 kamus.py menaikkan KeyError: 'kunci'. Anotasi dict[str, int] tidak berisi janji bahwa key "kunci" terisi — checker memilih memercayai pemakaian [...] daripada membanjiri kita laporan “key mungkin tidak ada” di setiap baris. Batas-batas pragmatis semacam ini, ditambah keberadaan Any dan kode tanpa anotasi, berarti: laporan bersih menaikkan keyakinan, bukan jaminan.

Strategi Adopsi Bertahap #

TahapTindakanDampak
1Jalankan checker bawaan (tanpa config) pada project lamaGalat paling jelas muncul; tidak ada perubahan kode
2Anotasi function publik dan titik batas dataSalah tipe dari luar tertangkap di gerbang
3Rapikan laporan bertahap, mulai dari error paling banyakLaporan menurun, disiplin terbangun
4Naikkan ketegasan lewat config (--strict atau pyproject.toml)Pemeriksaan penuh untuk kode baru

Dengan pemahaman ini, kita siap mengoperasikan checkernya sendiri secara praktis — lewat mypy di terminal dan Pyright di editor — pada chapter penutup Section ini.