Exclude<T, U> membuang anggota dari sebuah union type, Extract<T, U> hanya menyimpan anggota yang cocok, dan NonNullable<T> membuang null dan undefined. Ketiganya menyaring union; tidak ada yang menyentuh property objek.
Seperti setiap utility type, ketiganya hanya ada saat kompilasi. Baris terakhir ditolak oleh compiler; JavaScript yang dijalankan sama sekali tidak tahu apa itu Visible.
Sekilas Exclude, Extract, dan NonNullable
| Tipe | Menyimpan | Contoh | Hasil |
|---|---|---|---|
Exclude<T, U> | anggota T yang tidak bisa diberikan ke U | Exclude<"a" | "b" | "c", "a"> | "b" | "c" |
Extract<T, U> | anggota T yang bisa diberikan ke U | Extract<"a" | "b" | "c", "a" | "z"> | "a" |
NonNullable<T> | semuanya kecuali null dan undefined | NonNullable<string | null | undefined> | string |
"Bisa diberikan ke U" adalah frasa kuncinya. U tidak harus berupa daftar anggota yang persis; ia bisa berupa tipe yang lebih lebar yang cocok dengan beberapa anggota:
Memilih Anggota Union Berdasarkan Bentuk
Pada discriminated union, Extract dengan bentuk parsial mengambil varian yang punya bentuk itu. Inilah cara yang biasa untuk menamai satu varian dari union yang tidak kamu tulis sendiri:
Bentuk { type: "click" } adalah supertype dari varian click saja, jadi varian itulah yang disimpan Extract. Bentuk yang cocok dengan beberapa varian (misalnya { x: number } kalau ada dua varian yang memilikinya) menyimpan semuanya.
NonNullable
NonNullable<T> adalah versi tipe dari pemeriksaan !== null && !== undefined. Tipe ini muncul saat kamu menurunkan tipe dari sesuatu yang opsional:
User["manager"] adalah { name: string } | null | undefined (tanda ? menambahkan undefined), dan NonNullable membuang keduanya. Sejak TypeScript 5.5, compiler juga menyimpulkan predicate itu sendiri, jadi raw.filter((s) => s != null) sudah bertipe string[]; s is NonNullable<typeof s> yang eksplisit membuat maksudnya tetap terlihat dan berfungsi di versi yang lebih lama. Definisinya adalah type NonNullable<T> = T & {}: object type kosong {} menerima setiap nilai kecuali null dan undefined, jadi meng-intersect dengannya membuang persis kedua nilai itu.
Cara Ketiganya Dibangun
Exclude dan Extract adalah conditional type satu baris di library standar:
type Exclude<T, U> = T extends U ? never : T;
type Extract<T, U> = T extends U ? T : never;
Ketika T adalah union, conditional type diterapkan ke setiap anggota secara terpisah (terdistribusi), dan anggota never menghilang dari hasilnya. Jadi Exclude<"a" | "b", "a"> menjadi ("a" extends "a" ? never : "a") | ("b" extends "a" ? never : "b"), yaitu never | "b", yaitu "b". Halaman conditional type membahas distribusi secara mendetail.
Exclude vs Omit, Extract vs Pick
Namanya mudah tertukar karena kedua pasangan sama-sama "membuang" atau "menyimpan" sesuatu. Perbedaannya ada pada apa yang dikerjakan:
| Bekerja pada | Membuang atau menyimpan | Contoh | |
|---|---|---|---|
Exclude<T, U> | union | anggota union | Exclude<"a" | "b", "a"> → "b" |
Extract<T, U> | union | anggota union | Extract<"a" | "b", "a"> → "a" |
Omit<T, K> | object type | property | Omit<User, "password"> |
Pick<T, K> | object type | property | Pick<User, "name"> |
Memakai Exclude pada object type adalah kekeliruan yang umum, dan kegagalannya tidak terlihat:
Account tidak bisa diberikan ke "password", jadi Exclude menyimpannya utuh. Omit sendiri dibangun dari Exclude yang diterapkan pada key: Omit<T, K> adalah Pick<T, Exclude<keyof T, K>>. Lihat Omit untuk penjelasan lebih lanjut.
Exclude yang Lebih Ketat
Parameter kedua Exclude menerima tipe apa pun, jadi salah ketik tidak membuang apa pun dan tidak memunculkan error. Menambahkan constraint membuat compiler memeriksa bahwa yang kamu exclude benar-benar ada di union itu:
index.ts(7,37): error TS2344: Type '"drafft"' does not satisfy the constraint 'Status'.
Perbaiki ejaannya dan kodenya bisa dikompilasi, dengan Strict sama dengan "published" | "archived". Trik yang sama berlaku untuk Extract dan untuk Omit yang ketat (K extends keyof T).
Pertanyaan yang sering diajukan
Apa fungsi Exclude di TypeScript?
Exclude<T, U> membuang dari union T setiap anggota yang bisa diberikan ke U. Exclude<"a" | "b" | "c", "a"> adalah "b" | "c". Untuk membuang beberapa anggota, kirim union sebagai U: Exclude<T, "a" | "b">.
Apa bedanya Exclude dan Extract?
Keduanya berlawanan. Exclude<T, U> menyimpan anggota T yang tidak bisa diberikan ke U; Extract<T, U> hanya menyimpan anggota yang bisa. Untuk T dan U yang sama, gabungan kedua hasilnya membentuk T.
Apa bedanya Exclude dan Omit?
Exclude bekerja pada union type dan membuang anggota union. Omit bekerja pada object type dan membuang property. Exclude<User, "password"> tidak melakukan apa pun yang berguna (hasilnya User), sedangkan Omit<User, "password"> memberikan object type tanpa property itu. Omit dibangun di atas Exclude: ia meng-exclude key dari keyof T.
Apa fungsi NonNullable di TypeScript?
NonNullable<T> membuang null dan undefined dari sebuah tipe: NonNullable<string | null | undefined> adalah string. Tipe ini didefinisikan sebagai T & {}, karena {} menerima setiap nilai kecuali null dan undefined.
Kenapa Exclude tidak melaporkan error untuk anggota yang tidak ada?
Parameter keduanya tidak punya constraint, jadi tipe apa pun diterima, dan salah ketik seperti Exclude<Status, "drafft"> diam-diam tidak membuang apa pun. Definisikan versi yang lebih ketat, type StrictExclude<T, U extends T> = Exclude<T, U>, supaya compiler memeriksa bahwa U adalah bagian dari T.