narrowingassertionguardMenengah3 mnt baca

Assertion Function

Fungsi asserts x is T: lempar bila salah, persempit bila benar.

Assert: validasi atau mati

Type predicate menjawab "apakah ini X?" dengan boolean. Tapi banyak situasi butuh jawaban yang lebih galak: "pastikan ini X, atau program berhenti". Config yang wajib ada saat server start, parameter yang tidak boleh null di tengah pipeline, env var yang kalau hilang aplikasinya tidak boleh jalan setengah-setengah. Assertion function (asserts x is T) adalah versi galaknya predicate: tidak me-return boolean, dia throw kalau kondisi gagal, dan mempersempit tipe kalau lolos.

Alur validasi config langkah demi langkah

Skenario nyata: aplikasi Node yang butuh DATABASE_URL saat boot.

typescript
// Langkah 1: definisikan assertion function.
function pastikanAda(nilai: unknown, nama: string): asserts nilai is string {
  if (typeof nilai !== "string" || nilai === "") {
    throw new Error(`config wajib: ${nama} tidak ditemukan atau kosong`);
  }
}

function boot() {
  const dbUrl: unknown = process.env.DATABASE_URL;

  // Langkah 2: panggil sebagai statement biasa, bukan di dalam if.
  pastikanAda(dbUrl, "DATABASE_URL");

  // Langkah 3: setelah baris ini, dbUrl: string. Compiler yakin.
  // Kalau DATABASE_URL hilang, program sudah throw di atas,
  // jadi tidak akan pernah sampai sini dengan nilai yang salah.
  hubungkanDatabase(dbUrl); // aman, tanpa if/else bersarang
}

Bandingkan dengan predicate: kamu harus menulis if (!adalahString(dbUrl)) { throw ... } di setiap tempat. Assertion function membungkus pola "cek lalu throw" menjadi satu baris yang sekaligus mempersempit tipe untuk sisa fungsi.

Error compiler nyata

Assertion function return-nya void, jadi jangan dipakai di kondisi if:

typescript
if (pastikanAda(dbUrl, "DATABASE_URL")) {
  hubungkanDatabase(dbUrl);
}
text
error TS1345: An expression of type 'void' cannot be tested for truthiness.

Ini bukan bug di TypeScript, ini dia memberi tahu kamu salah pakai: assertion function dipakai sebagai statement berdiri sendiri, bukan ekspresi. Panggil, lalu lanjutkan kode. Kalau lolos, tipe sudah sempit. Kalau gagal, sudah throw sebelum baris berikutnya dieksekusi.

Predicate vs assertion: kapan yang mana

Type predicate (x is T)Assertion function (asserts x is T)
Returnbooleanvoid (tidak me-return apa-apa)
Saat kondisi gagalreturn falsethrow Error
Dipakaidi kondisi ifsebagai statement berdiri sendiri
Cocok untukvalidasi yang bisa ditangani (skip, fallback, default)validasi yang fatal kalau gagal (config, invariant)

Contoh berdampingan:

typescript
// Predicate: data kotor boleh dilewati.
const valid = daftar.filter(adalahUser);

// Assertion: data kotor di titik ini = bug, harus meledak sekarang juga.
pastikanAda(token, "AUTH_TOKEN");
kirimRequest(token);

Kapan dipakai di project nyata

  • Bootstrapping server: validasi env var dan config di awal, fail fast sebelum server menerima request dalam keadaan setengah jadi.
  • Invariant di domain logic: "di titik ini keranjang tidak boleh kosong", "user harus sudah login". Kalau invariant jebol, itu bug, bukan kondisi normal: throw sekarang, jangan diamkan.
  • Test dan skrip migrasi: Node punya assert bawaan, tapi tipenya longgar. Assertion function buatan sendiri memberi narrowing yang rapi plus pesan error yang jelas dan spesifik domain.

Catatan teknis: Untuk validasi data eksternal yang kompleks (misalnya body request di Express), pertimbangkan library seperti Zod: dia melakukan validasi runtime SEKALIGUS menurunkan tipe TypeScript otomatis, jadi kamu tidak perlu menulis predicate atau assertion manual satu per satu untuk setiap field.

Tantangan

Assert konfigurasi

Buat function pastikanAda<T>(x: T | null | undefined): asserts x is T yang throw "nilai wajib ada" bila null/undefined. Pakai untuk mengamankan akses config.apiUrl dari config: { apiUrl?: string } | undefined.

typescript
function pastikanAda<T>(x: T | null | undefined): asserts x is T {
  // throw bila null/undefined
}

declare const config: { apiUrl?: string } | undefined;

pastikanAda(config);
pastikanAda(config.apiUrl);
console.log(config.apiUrl.toUpperCase());

Kuis Bab

Uji pemahamanmu: Narrowing & Type Guard

Jawab 5 soal berikut, lalu tekan "Periksa Jawaban".

1.Apa itu narrowing?

2.Kenapa typeof x === "object" tidak cukup untuk memastikan x bukan null?

3.Apa itu discriminated union?

4.Apa beda type predicate (x is T) dan assertion function (asserts x is T)?

5.Apa arti keyof User untuk interface User { nama: string; umur: number }?