Tipe union mengizinkan satu nilai memiliki beberapa kemungkinan tipe, ditulis dengan tanda pipa seperti string | number. Dipadukan dengan tipe literal (literal type) yang hanya menampung satu nilai tertentu, union menjadi cara ringkas memodelkan pilihan yang terbatas. Topik ini membahas penulisan union, penyempitan tipe (narrowing) berdasarkan nilai, dan pemakaian tipe literal sebagai enum versi ringan.
Menggabungkan tipe dengan tanda pipa#
Tanda pipa menggabungkan dua tipe atau lebih menjadi satu tipe union. Nilai yang bertipe union boleh berupa salah satu anggotanya, dan compiler menolak nilai di luar itu semua. Union paling sederhana menggabungkan tipe primitif, misalnya string | number untuk pengenal yang kadang berupa kode teks dan kadang berupa nomor.
Berkas berikut menampilkan pengenal dalam dua bentuk berbeda sesuai tipe asalnya.
function tampilkanId(id: string | number): string {
if (typeof id === "string") {
return `Kode: ${id}`;
}
return `Nomor: ${id}`;
}
console.log(tampilkanId("TGR-091"));
console.log(tampilkanId(27));Kode: TGR-091
Nomor: 27Parameter id bertipe string | number, sehingga kedua pemanggilan diterima. Di dalam fungsi, pemeriksaan typeof id === "string" mempersempit tipe menjadi string pada blok if, sedangkan sisa alur otomatis diperlakukan sebagai number. Perilaku ini disebut penyempitan tipe karena tipe yang berlaku menyempit mengikuti alur program.
Anggota union tidak dibatasi dua, dan penulisannya cukup memperpanjang deretan tanda pipa. Union juga lazim digabungkan dengan null atau undefined ketika nilai memang bisa tidak ada. Yang perlu diingat adalah setiap anggota menambah kemungkinan yang harus ditangani, sehingga union yang panjang menandakan logika yang juga panjang. Karena itu, daftar kemungkinan yang disengaja dan terkendali lebih baik daripada union yang tumbuh tanpa arah.
Penyempitan berdasarkan nilai literal#
Pemeriksaan kesetaraan pada tipe literal juga menyempitkan union. Tipe yang anggotanya berupa nilai literal dapat diperiksa satu per satu dengan ===, dan compiler mengikuti cabang yang aktif. Deklarasi type Arah = ... pada berkas berikut membuat alias tipe (type alias), yaitu nama baru untuk suatu tipe yang dapat dipakai ulang di beberapa tempat.
type Arah = "utara" | "timur" | "selatan" | "barat";
function deltaKompas(arah: Arah): number {
if (arah === "utara") {
return 0;
}
if (arah === "timur") {
return 90;
}
if (arah === "selatan") {
return 180;
}
return 270;
}
console.log(deltaKompas("timur"));
console.log(deltaKompas("barat"));90
270Setiap cabang if mengembalikan nilai sesuai arah yang diperiksa, dan cabang terakhir menampung sisa anggota union. Apabila satu arah ditambahkan ke tipe Arah tanpa menambah cabang, compiler tidak melaporkan cabang yang terlewat, karena return 270 masih sah untuk nilai apa pun. Karena itu, pemeriksaan berbasis if paling cocok untuk union kecil yang anggotanya jarang berubah.
Penyempitan juga berlaku sebaliknya setelah cabang bersyarat selesai. Setelah pemeriksaan arah === "utara" tidak terpenuhi, compiler tahu arah bukan lagi "utara", sehingga cabang berikutnya bekerja dengan sisa anggota yang makin sedikit. Pola beruntun inilah yang membuat deretan if pada berkas di atas tetap rapi tanpa konversi apa pun. Urutan pemeriksaan bebas ditentukan, dan hasilnya selalu sama karena setiap nilai hanya cocok dengan satu anggota.
Tipe literal sebagai enum ringan#
Gabungan tipe literal berfungsi seperti enum versi ringan, yaitu daftar nilai yang sah untuk satu konsep. Deklarasi type StatusTiket = "menunggu" | "tercetak" | "kedaluwarsa" menetapkan tiga status yang sah, dan nilai di luar ketiganya ditolak compiler. Berbeda dengan kata kunci enum yang membuat objek nyata saat runtime, union literal tidak menambah apa pun pada hasil kompilasi.
type StatusTiket = "menunggu" | "tercetak" | "kedaluwarsa";
const statusHariIni: StatusTiket = "tercetak";
console.log(statusHariIni);tercetakEditor menampilkan ketiga status sebagai saran ketika variabel dianotasi, sehingga salah ketik tertangkap bahkan sebelum pemeriksaan tipe dijalankan. Penulisan "Ter" sebagai nilai akan ditolak dengan pesan yang menampilkan seluruh anggota yang sah. Pemakaian yang sama berlaku pada parameter fungsi, sehingga pemanggil selalu melihat daftar nilai yang diperbolehkan.
Anggota union literal tidak harus berupa teks. Angka juga sah sebagai literal, misalnya type Ukuran = 1 | 2 | 3 untuk pilihan ukuran yang terbatas, dan campuran teks dengan angka tetap boleh selama maknanya jelas. Nilai true dan false pun dapat dipakai sebagai anggota literal ketika keputusan biner perlu dibedakan dari boolean biasa. Meski demikian, teks tetap paling umum karena terbaca langsung pada log maupun pesan error.
Kesalahan umum#
Kesalahan pertama adalah memanggil properti atau metode yang tidak dimiliki seluruh anggota union. Compiler menolaknya karena keanggotaan union berarti gabungan kemampuan semua anggotanya, sedangkan properti yang hanya dimiliki sebagian tidak dapat dijamin ada. Pemeriksaan ini mencegah error yang seharusnya baru muncul saat program berjalan.
// tidak-dijalankan
function panjangNilai(nilai: string | number): number {
return nilai.length;
}# menampilkan pesan error tanpa mengeksekusi berkas
tsc --noEmit union-salah.tsunion-salah.ts(3,16): error TS2339: Property 'length' does not exist on type 'string | number'.
Property 'length' does not exist on type 'number'.Properti length hanya dimiliki string, sedangkan number tidak memilikinya, sehingga akses langsung pada union ditolak. Perbaikannya adalah menyempitkan tipe terlebih dahulu dengan typeof, lalu mengakses length di dalam blok yang tipenya sudah pasti string. Pola yang sama berlaku untuk metode milik tipe tunggal lain, misalnya toFixed yang hanya dimiliki number.
Kesalahan kedua adalah menugaskan nilai yang bukan anggota tipe literal. Pesan errornya justru berguna karena menampilkan seluruh anggota yang sah. Kasus ini sering terjadi ketika nilai berasal dari masukan yang belum diperiksa.
// tidak-dijalankan
type Skala = "rendah" | "sedang" | "tinggi";
const skalaHariIni: Skala = "ekstrem";# menampilkan pesan error tanpa mengeksekusi berkas
tsc --noEmit literal-salah.tsliteral-salah.ts(4,7): error TS2322: Type '"ekstrem"' is not assignable to type 'Skala'.Kesalahan ketiga yang lazim adalah lupa menyertakan undefined pada union bila sumber data memang bisa tidak menghasilkan apa pun. Union yang anggotanya kurang membuat pemeriksaan menjadi lebih longgar daripada yang seharusnya. Akibatnya, nilai undefined lolos ke pemakaian berikutnya dan menimbulkan error saat program berjalan.
Ringkasan#
- Tipe union ditulis dengan tanda pipa dan mengizinkan satu nilai berasal dari salah satu anggotanya.
- Penyempitan tipe terjadi melalui pemeriksaan
typeofmaupun perbandingan===terhadap nilai literal, dan hanya berlaku pada compiler. - Union dari tipe literal berfungsi sebagai enum ringan tanpa menambah objek apa pun saat runtime.
- Properti yang tidak dimiliki seluruh anggota union hanya boleh diakses setelah tipe disempitkan.
Referensi#
- Narrowing — The TypeScript Handbook — Panduan penyempitan union melalui pemeriksaan tipe dan nilai.
- Literal Types — The TypeScript Handbook — Penjelasan tipe dari nilai tunggal dan kombinasi union-nya.
- Everyday Types — The TypeScript Handbook — Panduan tipe harian, termasuk union dan tipe literal.