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.
// 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:
if (pastikanAda(dbUrl, "DATABASE_URL")) {
hubungkanDatabase(dbUrl);
}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) | |
|---|---|---|
| Return | boolean | void (tidak me-return apa-apa) |
| Saat kondisi gagal | return false | throw Error |
| Dipakai | di kondisi if | sebagai statement berdiri sendiri |
| Cocok untuk | validasi yang bisa ditangani (skip, fallback, default) | validasi yang fatal kalau gagal (config, invariant) |
Contoh berdampingan:
// 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
assertbawaan, 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.
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 }?