Menu

Intersection Type di TypeScript: Menggabungkan Tipe dengan &

Intersection type A & B menjelaskan nilai yang sekaligus A dan B, jadi ia punya setiap member dari keduanya. Pelajari cara menggabungkan tipe object dengan &, kenapa properti yang bertentangan menjadi never, bagaimana intersection dari union hanya menyisakan member yang sama, dan kapan sebaiknya memakai extends.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Intersection type, yang ditulis A & B, menjelaskan nilai yang sekaligus A dan B. Untuk tipe object, artinya nilai itu punya setiap properti dari keduanya. Dengan cara ini kamu menggabungkan tipe yang sudah ada tanpa menulis ulang propertinya.

Object kedua tidak menyertakan team, jadi hasilnya error kompilasi TS2322, dan baris berikutnya dari pesan error itu menyatakan Property 'team' is missing ... but required in type 'Employee'. Nilai Staff bisa dikirim ke mana pun Person atau Employee diharapkan.

Menggabungkan Tipe Object

& bekerja dengan campuran type alias, interface, dan tipe object inline apa pun, juga dengan parameter tipe generic. Kasus terakhir itulah yang sulit digantikan: fungsi yang menambahkan properti ke object apa pun yang diterimanya bisa menyatakannya dengan tepat.

Pemanggil tetap mendapat tipe persis dari apa yang dikirimnya (title, words) ditambah dua properti baru. interface ... extends tidak bisa menyatakan "apa pun T-nya, ditambah ini", karena interface tidak bisa meng-extend parameter tipe.

Properti yang Bertentangan Menjadi never

Kalau kedua sisi mendeklarasikan properti yang sama, tipe properti itu adalah intersection dari keduanya. Kalau tipe-tipe itu tidak punya nilai yang sama, propertinya menjadi never, dan compiler diam saja sampai kamu mencoba membuat sebuah nilai:

index.ts(7,22): error TS2322: Type 'string' is not assignable to type 'never'.

Error-nya menunjuk ke object, bukan ke tipe yang menyebabkannya, sehingga bug seperti ini lama dilacak. Mengarahkan kursor ke r.id di editor menunjukkan tipenya: never. Kalau properti yang bertentangan adalah tag literal, seperti di type Shape = { kind: "circle" } & { kind: "square" }, TypeScript bertindak lebih jauh dan menyusutkan seluruh intersection menjadi never. Membaca properti dari nilai seperti itu lalu menjelaskan alasannya: Property 'kind' does not exist on type 'never'. The intersection 'Shape' was reduced to 'never' because property 'kind' has conflicting types in some constituents.

interface ... extends justru menangkap konflik yang sama di deklarasinya, dengan error TS2430. Itulah perbedaan praktis utama antara keduanya; halaman interface vs type membandingkannya berdampingan.

Tumpang Tindih yang Kompatibel Mempersempit Properti

Kalau dua tipe properti memang beririsan, hasilnya adalah irisan itu. Ini berguna, bukan error:

Bagian kedua menunjukkan apa yang dilakukan & pada union: ia menyisakan member yang dimiliki kedua sisi. Memandang tipe sebagai himpunan nilai membuat perilaku ini mudah ditebak. A | B adalah gabungan dari dua himpunan, A & B adalah irisannya, dan irisan yang kosong adalah never.

Intersection vs Union

Nama-namanya berasal dari teori himpunan, dan terasa terbalik kalau diterapkan pada properti object:

A | B (union)A & B (intersection)
Nilainya adalahA atau BA dan B
Himpunan nilai yang diizinkanlebih besarlebih kecil
Properti yang bisa dipakaihanya yang ada di keduanyasemua dari masing-masing
string dengan numberstring | numbernever
"a" | "b" dengan "b" | "c""a" | "b" | "c""b"

Intersection dari tipe object punya lebih banyak properti justru karena ia mengizinkan lebih sedikit nilai: hanya object yang punya semuanya.

Intersection vs extends

type C = A & Binterface C extends A, B
Dipakai dengantipe apa pun, termasuk union dan parameter tipetipe object dengan member yang diketahui secara statis
Properti yang bertentangandiam-diam menjadi nevererror TS2430 atau TS2320 di deklarasinya
Hasilsebuah intersection, diperiksa bagian demi bagiansatu tipe bernama yang datar, dengan hubungan yang disimpan di cache
Komposisi besarbisa memperlambat pemeriksaan tipelebih disarankan oleh halaman Performance di wiki TypeScript

Untuk menggabungkan beberapa type alias object, & sudah idiomatis dan baik-baik saja. Untuk tipe yang dibangun dari banyak bagian, atau tipe API publik, extends memberi error lebih awal dan pemeriksaan tipe yang lebih ringan.

Intersection dengan Tipe Primitif: Branding

Intersection antara tipe primitif dan tipe object tidak menghasilkan never: string & { readonly __brand: "UserId" } adalah string yang membawa penanda tambahan yang hanya ada saat kompilasi. Tidak ada string sungguhan yang punya properti itu, dan memang itulah intinya: hanya kode yang sengaja menegaskan brand itu yang bisa membuatnya, jadi string biasa atau OrderId tidak bisa lagi dikirim ke tempat yang mengharapkan UserId. Teknik ini punya halamannya sendiri, branded type.

Pertanyaan yang sering diajukan

Apa itu intersection type di TypeScript?

Tipe yang ditulis A & B, yang nilainya harus memenuhi A dan B sekaligus. Untuk tipe object, artinya nilai itu punya setiap properti A dan setiap properti B. Ini cara yang umum untuk menggabungkan dua type alias menjadi satu.

Apa perbedaan union dan intersection?

Union A | B berarti "salah satu": nilainya bisa berupa A atau B, dan kamu hanya bisa memakai apa yang dimiliki keduanya sampai kamu melakukan narrowing. Intersection A & B berarti "keduanya": nilainya punya semua yang ada di kedua tipe. Pada tipe object, union menerima lebih banyak nilai, sedangkan intersection punya lebih banyak properti.

Kenapa intersection type-ku menjadi never?

Karena tidak ada nilai yang bisa memenuhi kedua sisi. string & number adalah never, dan { id: string } & { id: number } membuat id bertipe string & number, sehingga propertinya never dan tidak ada object yang bisa dibuat. Kalau dua tipe object punya tag literal yang sama dengan nilai berbeda (kind: "circle" dan kind: "square"), seluruh intersection-nya menyusut menjadi never.

Sebaiknya pakai intersection atau extends?

Keduanya menggabungkan tipe object. interface X extends A, B melaporkan properti yang bertentangan di deklarasinya dan direkomendasikan tim TypeScript untuk menyusun tipe object yang besar. & bekerja dengan tipe apa pun, termasuk union dan parameter generic, yang tidak bisa digabungkan dengan extends. Pakai & untuk type alias dan helper generic, dan extends saat membangun interface.

Bagaimana cara menggabungkan dua tipe object di TypeScript?

Tulis type Merged = A & B. Untuk nilainya saat runtime, lakukan spread pada kedua object: const merged: A & B = { ...a, ...b }. Kalau A dan B punya properti yang sama dengan tipe berbeda, tipe properti itu menjadi never; pakai Omit<A, keyof B> & B kalau properti object kedua seharusnya menggantikan properti object pertama.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai