Type Predicate
Fungsi boolean custom yang mempersempit tipe: x is T.
Kenapa predicate itu ada
Narrowing bawaan (typeof, in, instanceof) cuma ngerti struktur dangkal. Tapi validasi dunia nyata sering butuh logika domain: "apakah objek ini User yang valid?", "apakah string ini email?", "apakah array ini sudah bersih dari undefined?". Type predicate (x is T) adalah cara kamu mengajari compiler pengecekan custom: fungsi boolean biasa yang return-nya sekaligus janji ke compiler, "kalau true, maka x bertipe T".
Alur validasi API response langkah demi langkah
Skenario nyata: fetch mengembalikan data yang tipenya tidak kamu ketahui. Kamu mau mengubahnya jadi User yang terverifikasi, tanpa berbohong ke compiler.
interface User { id: number; nama: string; email: string }
// Langkah 1: tulis predicate. Return type-nya "x is User", BUKAN boolean.
function adalahUser(x: unknown): x is User {
if (typeof x !== "object" || x === null) return false;
const o = x as Record<string, unknown>; // cek struktur, bukan klaim tipe akhir
return (
typeof o.id === "number" &&
typeof o.nama === "string" &&
typeof o.email === "string"
);
}
async function ambilUser(id: number): Promise<User | null> {
const res = await fetch(`/api/user/${id}`);
const raw: unknown = await res.json(); // Langkah 2: anggap unknown dulu.
// Langkah 3: predicate mempersempit unknown jadi User. Tanpa casting!
if (adalahUser(raw)) {
return raw; // raw: User, terverifikasi saat runtime
}
return null; // data rusak, tolak dengan sopan
}Tanpa predicate, kamu cuma punya dua pilihan buruk: as User (berbohong ke compiler, dibahas di modul assertion) atau menulis if validasi yang panjang dan tidak reusable di setiap tempat.
Predicate untuk filter array: kasus paling laris
const campur: (string | undefined)[] = ["a", undefined, "b"];
function bukanUndefined(x: string | undefined): x is string {
return x !== undefined;
}
const bersih: string[] = campur.filter(bukanUndefined);Tanpa predicate, campur.filter(x => x !== undefined) hasilnya tetap (string | undefined)[]. Error-nya muncul belakangan, saat elemennya dipakai:
function kirimSemua(daftar: string[]) { /* ... */ }
kirimSemua(campur.filter(x => x !== undefined));error TS2345: Argument of type '(string | undefined)[]' is not assignable
to parameter of type 'string[]'.Predicate membuat .filter() "mengerti" tipe hasil saringannya. Ini trik yang dipakai di hampir semua codebase TypeScript yang matang.
Kapan dipakai di project nyata
- Validasi data eksternal: response API,
JSON.parse, pesan WebSocket, isilocalStorage. Semua mulai dariunknown, predicate yang mengubahnya jadi tipe domain dengan bukti runtime. - Filter koleksi: buang null, undefined, atau item invalid dari array sebelum diolah, dengan tipe hasil yang bersih tanpa sisa union.
- Type guard yang reusable: daripada mengulang
typeof x === "object" && x !== null && ...di sepuluh tempat, bungkus sekali jadiadalahX(x)dan pakai di mana-mana.
Catatan teknis: Predicate adalah klaim yang dipercaya compiler tanpa verifikasi. Kalau logikanya salah (return
trueuntuk yang bukan User), type safety jebol diam-diam dan tidak ada yang protes. Tulis predicate dengan hati-hati, dan unit test predicate yang kritis seperti kamu mengetes fungsi bisnis.
Tantangan
Filter nilai valid
Buat type predicate adalahAngka(x: unknown): x is number yang true hanya untuk number bukan NaN. Pakai untuk memfilter [1, "a", 2, NaN, 3] menjadi number[] berisi [1, 2, 3].
function adalahAngka(x: unknown): x is number {
// true bila typeof number dan bukan NaN
}
const data: unknown[] = [1, "a", 2, NaN, 3];
const angka: number[] = data.filter(adalahAngka);
console.log(angka); // [1, 2, 3]