Intersection type, ditulis dengan &, menggabungkan beberapa tipe jadi satu: nilainya harus memenuhi semua tipe itu sekaligus. keyof mengambil nama-nama properti sebuah tipe sebagai union. Bayangkan formulir pendaftaran yang terdiri dari dua lembar yang dijepret jadi satu: lembar posisi dan lembar warna. Formulirnya baru sah kalau dua lembar itu terisi semua. Daftar nama kolom di formulir itu adalah keyof-nya.
🧪 Tebakan cepat:
type Titik = Posisi & Warna. Apakah objek{ x: 1, y: 2 }lolos sebagaiTitik? Kalau&diganti|? Tebak dulu, jawabannya ada di bagian "& lawan |".
Gabungkan dua tipe dengan &
type Posisi = {
x: number;
y: number;
};
type Warna = {
warna: string;
};
type Titik = Posisi & Warna; // harus punya x, y, dan warna
const titik: Titik = { x: 1, y: 2, warna: "merah" }; // lolos
const tanpaWarna: Titik = { x: 1, y: 2 }; // error
error TS2322: Type '{ x: number; y: number; }' is not assignable to type 'Titik'.
Property 'warna' is missing in type '{ x: number; y: number; }' but required in type 'Warna'.
Posisi & Warnadibaca "Posisi dan Warna".Titikpunya semua properti dari dua tipe itu:x,y, danwarna.PosisidanWarnatetap tipe tersendiri. KalauPosisinanti dapat properti baru,Titikotomatis ikut.- Pesan error-nya menyebut lembar mana yang belum lengkap:
warnawajib karena ada diWarna.
& lawan |
Union | (sudah kamu kenal) artinya "salah satu". Intersection & artinya "semuanya".
| Objek | Posisi & Warna |
Posisi | Warna |
|---|---|---|
{ x: 1, y: 2, warna: "merah" } |
lolos | lolos |
{ x: 1, y: 2 } |
error, warna kurang |
lolos sebagai Posisi |
{ warna: "merah" } |
error, x dan y kurang |
lolos sebagai Warna |
{ x: 1 } |
error, y kurang |
error, y kurang |
Jadi { x: 1, y: 2 } ditolak sebagai Posisi & Warna, tapi lolos sebagai Posisi | Warna karena sudah lengkap sebagai Posisi. Itu jawaban tebakannya.
Hati-hati kalau dua tipe punya properti bernama sama dengan tipe berbeda. Di type A = { x: number } & { x: string }, x harus number sekaligus string. Gak ada nilai seperti itu, jadi A["x"] bertipe never, tipe yang gak punya nilai sama sekali.
keyof dan T[K]
keyof mengubah sebuah tipe jadi union nama-nama propertinya:
type Kunci = keyof Titik; // "x", "y", atau "warna"
type TipeWarna = Titik["warna"]; // string
type TipeX = Titik["x"]; // number
const k: Kunci = "warna"; // lolos
keyof Titikberisi"x","y", dan"warna". Nilai lain, misalnya"z", ditolak.Titik["warna"]memakai kurung siku seperti mengambil isi object, tapi untuk tipe. Hasilnya tipe properti itu,string. Ini disebut indexed access type.
getValue: kunci yang pasti ada
Gabungkan keyof dengan generic, dan kamu dapat fungsi yang cuma menerima kunci yang benar-benar ada:
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
getValue({ x: 1, y: 2, warna: "merah" }, "warna"); // tipe string, nilai "merah"
getValue({ x: 5, y: 10, warna: "biru" }, "x"); // tipe number, nilai 5
getValue({ x: 1, y: 2, warna: "merah" }, "z"); // error
error TS2345: Argument of type '"z"' is not assignable to parameter of type '"warna" | "x" | "y"'.
Tadalah tipe objeknya, disimpulkan dari argumen pertama.K extends keyof TmembatasiKke nama properti milikT. Daftar di pesan error-nya diurutkan abjad, isinya tetap tiga kunci tadi.T[K]adalah tipe properti yang diminta. Kunci"x"menghasilkannumber, kunci"warna"menghasilkanstring.obj[key]membaca properti yang namanya ada dikey. Jangan tulisobj.keyatauobj["warna"]: itu membaca properti tetap, bukan yang dikirim.
⚠️ Jebakan yang sering ketemu
- Mengira
&itu "atau".&minta semua properti dari semua tipe. Kalau maksudnya salah satu, pakai|. - Menulis ulang isi tipe.
type Titik = { x: number; y: number; warna: string }bentuknya sama, tapi gak ikut berubah kalauPosisiberubah. Gabungkan tipe yang sudah ada:Posisi & Warna. - Kunci dipatok di dalam fungsi.
return obj["warna"]selalu mengembalikan warna, jadigetValue(..., "x")ikut dapat warna. Pakaiobj[key]. - Properti sama, tipe beda.
{ x: number } & { x: string }membuatxbertipenever, jadi gak ada objek yang bisa memenuhinya.
TL;DR: A & B (intersection) artinya nilainya harus punya semua properti A dan semua properti B: Titik = Posisi & Warna wajib punya x, y, dan warna. A | B (union) cukup salah satunya. Properti sama dengan tipe bertabrakan jadi never. keyof T adalah union nama properti T, dan T[K] adalah tipe properti K. getValue<T, K extends keyof T>(obj: T, key: K): T[K] mengembalikan obj[key] dengan tipe yang tepat, dan menolak kunci yang gak ada seperti "z".