TypeScript Discriminated Unions Mendalam dan Exhaustiveness
Definisi
Discriminated union adalah union dari object type yang punya property literal bersama (kind/type/status) sebagai "discriminator". TypeScript bisa narrowing otomatis di switch. exhaustiveness check via never memastikan kita menangani semua kasus — kalau ada kasus lupa, compile error.
Kapan dipakai
Pakai untuk state machine, response API (success/error/loading), atau event dengan bentuk berbeda. Sangat berguna agar penambahan kasus baru tidak luput ditangani.
Cara kerja
Union dengan discriminator
type Res =
| { status: 'loading' }
| { status: 'success'; data: string }
| { status: 'error'; message: string };
function view(r: Res) {
switch (r.status) {
case 'loading': return '...';
case 'success': return r.data.toUpperCase(); // r: success
case 'error': return r.message;
}
}Exhaustiveness
function assertNever(x: never): never { throw new Error('unhandled: ' + x); }
function view2(r: Res) {
switch (r.status) {
case 'loading': return 'L';
case 'success': return r.data;
case 'error': return r.message;
default: return assertNever(r); // kalau ada kasus baru, error di sini
}
}Struktur alur
Loading diagram...
Catatan implementasi
- Discriminator harus literal type (
'success'), bukanstringumum. assertNeverhanya menerimanever; kalau switch belum lengkap, sisa jadinevergagal.- Berguna saat refactoring: tambah kasus di type, semua switch wajib di-update.
- Bisa digabung dengan
infer/Excludeuntuk manipulasi tipe lanjut.
Kesalahan umum
- Discriminator string biasa: TS tak bisa narrow; pakai literal union.
- Lupa default assertNever: kasus baru lolos tanpa peringatan.
- Mutable discriminator: jangan ubah
r.statusdi tengah switch. - Over-narrowing: terlalu dalam hingga tipe sulit dibaca.