Konfigurasi pada Chapter Setup Proyek TypeScript menuliskan "strict": true tanpa banyak upacara — dan janjinya kita tunda ke sini. Strict mode adalah kesepakatan untuk berhenti berpura-pura bahwa nilai selalu ada dan tipenya selalu jelas: variabel yang bisa null dinyatakan bisa null, galat yang ditangkap diperlakukan asing sampai dibuktikan bentuknya. Imbalannya satu kelas bug utuh — kecelakaan null dan undefined yang paling sering menjatuhkan aplikasi JavaScript — pindah dari masa runtime ke masa kompilasi. Chapter ini membuktikannya lewat tiga demonstrasi: dua yang gagal di kedua tahap, dan satu yang hanya gagal di mata pemeriksa tipe.

Sekantong Saklar dalam Satu Nama #

strict bukan satu saklar melainkan kantong berisi beberapa saklar yang menyala bersama. Versi TypeScript yang kita pakai menyalakan keluarga lengkap; tiga anggotanya paling sering terasa dalam kode sehari-hari:

SaklarYang diawasiTanpa saklar ini
strictNullChecksnull dan undefined bukan anggota tipe apa pun kecuali dinyatakanstring boleh berisi null diam-diam
noImplicitAnyparameter tanpa tipe ditolak, bukan dibiarkan anyparameter tak bertipe meloloskan apa pun
useUnknownInCatchVariablesvariabel catch bertipe unknown, bukan anygalat ditangani seolah pasti punya .message

Cara membaca tabelnya: kolom kanan adalah dunia tanpa strict — kode terlihat benar, kompiler tidak bersuara, dan masalah muncul belakangan. Daftar lengkap saklar dalam kantong strict terdokumentasi di halaman opsi TSConfig . Pada proyek baru, jawaban yang tepat sudah diberikan konfigurasi kita: nyalakan dari hari pertama. Mematikannya setelah aktif justru pekerjaan — dan itu sengaja.

Tipe yang Jujur tentang Null #

Demonstrasi pertama: sebuah fungsi yang menerima nama berupa string | null lalu membaca panjangnya tanpa memeriksa:

ts
function panjangNama(nama: string | null): number {
  return nama.length;
}

console.log(panjangNama(null));

Periksa dengan tsc dalam mode ketat:

bash
npx --yes [email protected] --noEmit --strict nol.ts
echo "kode keluar: $?"

Output yang Dihasilkan:

text
nol.ts(2,10): error TS18047: 'nama' is possibly 'null'.
kode keluar: 1

Sistem tipe menolak baris kedua: nama mungkin null, dan .length pada null tidak ada. Tanpa strictNullChecks, deklarasi string | null tetap sah namun pemeriksaan berhenti di situ — null dianggap cocok dengan apa saja. Untuk melihat apa yang sebenarnya dicegah, jalankan berkas yang sama dengan Node.js, yang meniadakan seluruh anotasi tipe dan mengeksekusi logikanya mentah-mentah:

ts
function panjangNama(nama: string | null): number {
  return nama.length;
}

console.log(panjangNama(null));

Output yang Dihasilkan:

text
  return nama.length;
              ^

TypeError: Cannot read properties of null (reading 'length')

Node.js v24.21.0

Inilah kecelakaan yang sama, kini terjadi sungguhan: TypeError tepat seperti yang diperkirakan pemeriksa tipe. Perbaikannya bukan trik — cukup memperlakukan null sebagai warga kelas satu yang perlu diperiksa sebelum dipakai:

ts
function panjangNama(nama: string | null): number {
  if (nama === null) {
    return 0;
  }
  return nama.length;
}

console.log(panjangNama("TypeScript"));
console.log(panjangNama(null));

Output yang Dihasilkan:

text
10
0

Setelah pemeriksaan nama === null lolos, TypeScript menyempitkan tipe menjadi string — perilaku type narrowing — sehingga baris keempat dianggap sah. Kompilasi bersih, eksekusi bersih, dan yang paling penting: kewajiban memeriksa dipaksakan oleh kompiler, bukan oleh ingatan kita.

Nilai yang Mungkin Absen #

Kasus kedua lebih halus karena tidak ada null yang tampak di signatures. Array.prototype.find mengembalikan elemen yang dicari — atau undefined bila tidak ketemu:

ts
const daftar: string[] = ["Budi", "Siti"];

const ketemu = daftar.find((nama) => nama === "Dewi");

console.log(ketemu.length);
bash
npx --yes [email protected] --noEmit --strict cari.ts
echo "kode keluar: $?"

Output yang Dihasilkan:

text
cari.ts(5,13): error TS18048: 'ketemu' is possibly 'undefined'.
kode keluar: 1

Kali ini undefined datang dari dalam library standar, bukan dari kode kita — dan tetap ditangkap karena deklarasi find di TypeScript sendiri jujur tentang kemungkinan itu. Nama “Dewi” sengaja dipilih karena tidak ada di daftar; bila berkas ini dijalankan dengan Node.js, kegagalan runtime-nya mengikuti pola TypeError yang sama seperti demo pertama. Perbaikannya kembali pada pemeriksaan eksplisit:

ts
const daftar: string[] = ["Budi", "Siti"];

const ketemu = daftar.find((nama) => nama === "Dewi");

if (ketemu === undefined) {
  console.log("nama tidak ditemukan");
} else {
  console.log(`nama ditemukan: ${ketemu}`);
}

Output yang Dihasilkan:

text
nama tidak ditemukan

Perhatikan pola yang berulang: undefined dan null tidak dihapus dari kehidupan kita — mereka hanya dipindahkan ke depan pintu, tempat kompiler berdiri, sehingga setiap pemakaian tanpa pemeriksaan menjadi galat kompilasi, bukan kejutan tengah malam.

Galat yang Hanya Terlihat oleh Pemeriksa Tipe #

Demonstrasi ketiga memperlihatkan sesuatu yang berbeda: kode yang berjalan sempurna namun tetap ditolak kompiler. Di JavaScript, catch menerima galat apa pun; di TypeScript ketat, variabelnya bertipe unknown — bentuk paling jujur untuk “sesuatu yang tidak kita ketahui”:

ts
function bacaKonfigurasi(): string {
  throw new Error("berkas konfigurasi tidak ditemukan");
}

try {
  console.log(bacaKonfigurasi());
} catch (error) {
  console.log(`terjadi galat: ${error.message}`);
}
bash
npx --yes [email protected] --noEmit --strict tangkap.ts
echo "kode keluar: $?"

Output yang Dihasilkan:

text
tangkap.ts(8,33): error TS18046: 'error' is of type 'unknown'.
kode keluar: 1

Kompiler menolak error.message karena unknown tidak menjanjikan properti apa pun. Namun jalankan dengan Node.js — yang menghapus anotasi dan mengeksekusi langsung:

ts
function bacaKonfigurasi(): string {
  throw new Error("berkas konfigurasi tidak ditemukan");
}

try {
  console.log(bacaKonfigurasi());
} catch (error) {
  console.log(`terjadi galat: ${error.message}`);
}

Output yang Dihasilkan:

text
terjadi galat: berkas konfigurasi tidak ditemukan

Berhasil — dan justru di situlah pelajarannya. Kode ini benar hari ini karena galat yang dilempar memang Error. Besok, ketika sebuah string atau angka ikut terlempar, properti .message akan mengembalikan undefined dan log kita berbohong tanpa suara. Pemeriksa tipe menolaknya bukan karena salah hari ini, melainkan karena tidak terbukti benar untuk semua kemungkinan. Versi yang terbukti mengungkap bentuknya lebih dulu:

ts
function bacaKonfigurasi(): string {
  throw new Error("berkas konfigurasi tidak ditemukan");
}

try {
  console.log(bacaKonfigurasi());
} catch (error) {
  if (error instanceof Error) {
    console.log(`terjadi galat: ${error.message}`);
  }
}

Output yang Dihasilkan:

text
terjadi galat: berkas konfigurasi tidak ditemukan

Pola instanceof ini adalah salah satu cara menyempitkan unknown; panduan lengkapnya ada di handbook TypeScript tentang unknown .

Menimbang Ketatnya #

Kejujuran punya harga yang layak diakui. Strict mode menuntut pemeriksaan yang dulu bisa ditunda; kode yang bersinggungan dengan data dari luar — JSON hasil fetch, baris query — perlu diberi bentuk sebelum dipakai, dan terkadang itu terasa seperti birokrasi untuk satu baris yang “jelas-jelas benar”. Namun pengalaman tim besar konsisten pada satu arah: biaya pemeriksaan di meja jauh lebih murah daripada biaya kegagalan di produksi, dan disiplin yang dipaksakan kompiler menjadi kebiasaan setelah beberapa minggu. Untuk itu konfigurasi pada chapter setup menyalakannya sejak awal — dan seluruh sisa Course ini menuliskan kode seolah strict selalu menyala.

Kita kini punya pertanyaan praktis yang belum terjawab: bagaimana persisnya berkas .ts ini dijalankan Node.js? Ada dua jalan — langsung dengan penghapusan tipe, atau lewat kompilasi — dan keduanya punya tempatnya masing-masing. Chapter Menjalankan TypeScript di Node.js membedah keduanya.