Union type mendaftar beberapa alternatif dengan |: nilai bertipe string | number berupa string atau number. Union adalah cara TypeScript menjelaskan nilai yang memang bisa punya lebih dari satu bentuk, dan compiler mewajibkanmu mengecek bentuk mana yang kamu pegang sebelum memakai apa pun yang khusus untuk bentuk itu.
Di dalam setiap cabang pemeriksaan typeof, id hanya punya satu tipe. Langkah itu disebut narrowing, dan itulah yang membuat union praktis dipakai.
Hanya Member Bersama yang Boleh Dipakai
Sebelum narrowing, kamu hanya bisa memakai apa yang didukung setiap anggota union. toString() ada di string maupun number, jadi boleh dipakai; toUpperCase() hanya ada di string:
index.ts(3,16): error TS2339: Property 'toUpperCase' does not exist on type 'string | number'.
Property 'toUpperCase' does not exist on type 'number'.
Baris kedua menyebutkan anggota yang tidak punya property itu. Aturan yang sama berlaku ke arah sebaliknya: nilai string | number tidak bisa diberikan ke parameter bertipe string (TS2345), karena nilainya mungkin number. Union menerima lebih banyak nilai, dan sebagai gantinya membatasi apa yang bisa kamu lakukan dengan nilai itu sampai kamu memeriksanya.
Mempersempit Union
Narrowing memakai pemeriksaan JavaScript biasa. TypeScript mengikuti alur kontrol dan membuang anggota yang sudah tersingkir, jadi setelah pemeriksaan terakhir hanya satu anggota yang tersisa:
| Pemeriksaan | Mempersempit | Cocok untuk |
|---|---|---|
typeof x === "string" | ke tipe primitifnya | string, number, boolean, bigint, symbol, undefined, function |
x === null, x === "a" | ke nilai yang dibandingkan | null, undefined, anggota literal |
Array.isArray(x) | ke anggota array | array |
x instanceof Date | ke class-nya | instance class |
"meow" in x | ke anggota yang punya property itu | object type |
x.kind === "circle" | ke anggota dengan penanda itu | discriminated union |
isCat(x) (mengembalikan x is Cat) | ke apa yang dinyatakan fungsi itu | apa saja, logika khusus |
Daftar lengkap bentuk narrowing ada di halaman type narrowing.
Union dari Tipe Literal
Union dari nilai literal adalah himpunan tertutup dari nilai yang diizinkan. Inilah union yang paling umum dalam kode nyata:
type Status = "idle" | "loading" | "success" | "error";
type Dice = 1 | 2 | 3 | 4 | 5 | 6;
type Toggle = "on" | "off" | boolean; // boolean is itself true | false
let current: Status = "idle";
current = "loading"; // fine
current = "finished"; // error TS2322: Type '"finished"' is not assignable to type 'Status'.
Membandingkan dengan literal akan mempersempit tipe: setelah if (current === "error"), cabang else tahu bahwa current adalah salah satu dari tiga nilai lainnya. Union literal menggantikan enum di banyak codebase; lihat tipe literal untuk as const dan cara menurunkan union seperti itu dari sebuah array.
Union dari Object Type
Ketika anggotanya berupa object type, property yang dimiliki semuanya bisa langsung dipakai. Untuk yang lainnya, cek dulu keberadaan property itu dengan in:
Untuk union dari beberapa bentuk object, pola yang lebih rapi adalah property literal bersama seperti kind: "cat" / kind: "fish". Memeriksa satu property itu mempersempit seluruh object, dan switch atas property itu bisa diperiksa kelengkapannya. Pola itu disebut discriminated union.
Array dan Union
Letak tanda kurung mengubah artinya sama sekali:
| Tipe | Artinya | Contoh nilai |
|---|---|---|
(string | number)[] | array yang setiap elemennya berupa string atau number | [1, "two", 3] |
string[] | number[] | array yang hanya berisi string, atau array yang hanya berisi number | ["a", "b"] |
string | number[] | sebuah string, atau array berisi number (| mengikat lebih longgar daripada []) | "text" |
Saat melakukan iterasi pada (string | number)[], setiap elemen bertipe union dan perlu dipersempit, seperti di callback reduce di atas. Method seperti map dan filter juga bekerja pada string[] | number[], dengan callback yang menerima string | number.
Union dengan null dan undefined
Union yang paling umum dari semuanya adalah "sebuah nilai atau tidak ada": string | null, User | undefined. Itulah yang dikembalikan Array.prototype.find dan Map.prototype.get, dan property opsional name?: string terbaca sebagai string | undefined. Cara menanganinya dengan ?., ?? dan pemeriksaan null punya halamannya sendiri: null dan undefined.
Untuk membuang anggota dari union yang sudah ada di tingkat tipe, pakai utility bawaan: Exclude<"a" | "b" | "c", "a"> adalah "b" | "c", dan NonNullable<string | null> adalah string.
Pertanyaan yang sering diajukan
Apa itu union type di TypeScript?
Tipe yang terdiri dari beberapa alternatif yang digabung dengan |. Nilai bertipe string | number bisa berupa string atau number. Compiler hanya membolehkanmu memakai apa yang dimiliki semua anggotanya sampai kamu mempersempit nilai itu ke satu anggota dengan pemeriksaan seperti typeof value === "string".
Kenapa TypeScript bilang sebuah property tidak ada pada union type?
Karena setidaknya satu anggota union tidak memilikinya. Error TS2339, misalnya Property 'toUpperCase' does not exist on type 'string | number', berarti nilainya mungkin berupa number, yang tidak punya toUpperCase. Persempit dulu (typeof, in, Array.isArray, instanceof, atau pemeriksaan diskriminan), lalu pakai property yang khusus untuk anggota itu.
Bagaimana cara mendeklarasikan array yang berisi lebih dari satu tipe?
Letakkan union di dalam tanda kurung: (string | number)[] atau Array<string | number>, di mana setiap elemen bisa berupa salah satu tipe. string[] | number[] berbeda: seluruh array berisi string semua atau number semua. Tanpa tanda kurung, string | number[] berarti sebuah string atau array berisi number.
Apa perbedaan union type dan intersection type?
Union A | B adalah nilai yang berupa salah satu tipe, jadi kamu hanya bisa memakai apa yang dimiliki keduanya. Intersection A & B adalah nilai yang sekaligus berupa kedua tipe, jadi ia punya semua member dari keduanya. Untuk object type, A | B menerima lebih banyak nilai, sedangkan A & B mewajibkan lebih banyak property.
Bagaimana cara mengecek tipe mana yang dimiliki nilai union?
Pakai pemeriksaan runtime yang dipahami TypeScript: typeof x === "string" untuk tipe primitif, Array.isArray(x) untuk array, x instanceof Date untuk class, "prop" in x untuk bentuk object, atau x.kind === "circle" ketika anggotanya punya penanda literal yang sama. Untuk logika khusus, tulis fungsi type guard yang mengembalikan x is T.