Omit<T, K> adalah utility type bawaan yang membuat object type berisi semua property T kecuali key yang tercantum di K. Contoh klasiknya: tipe User yang punya password, dan versi publiknya yang tidak punya.
PublicUser selalu sinkron dengan User: tambahkan field ke User dan field itu muncul juga di PublicUser, sementara password tetap tidak ikut. Omit hanya bekerja pada tipe; destructuring di toPublic itulah yang menghapus property dari object yang sebenarnya.
Meng-omit Beberapa Key
Argumen kedua adalah union, jadi daftarkan key sebanyak yang kamu perlukan dengan |:
"Tipe tanpa field yang dibuat server" adalah pemakaian Omit yang paling umum dalam kode nyata: satu tipe model, lalu Omit menurunkan bentuk input untuk form pembuatan data dan request API.
Bagaimana Omit Didefinisikan
Omit dibangun dari dua utility type lain:
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
Baca dari dalam: keyof T adalah union semua key, Exclude<keyof T, K> menghapus key yang tidak diinginkan dari union itu, dan Pick mempertahankan key yang tersisa. Jadi Omit<User, "password"> adalah Pick<User, "id" | "name" | "email">. Karena Pick melakukan mapping atas key milik T, modifier readonly dan ? pada property yang tersisa tetap dipertahankan.
Perhatikan batasannya: K extends keyof any, bukan K extends keyof T. Detail itulah yang dibahas di bagian berikutnya.
Omit Tidak Mengecek Apakah Key-nya Ada
keyof any adalah string | number | symbol, jadi Omit menerima key apa pun, termasuk key yang tidak dimiliki T. Salah ketik tetap lolos kompilasi dan tidak menghapus apa pun:
Tidak ada error di mana pun, dan password-nya ikut terkirim di JSON. Batasan yang longgar ini dipilih dengan sengaja saat Omit ditambahkan di TypeScript 3.5. Alasan timnya: di kode generic, key yang di-omit sering tidak berasal dari T (fungsi yang menggabungkan dua object bisa memberi tipe argumen rest-nya sebagai Omit<T1, keyof T2>), Exclude juga tidak membatasi parameter keduanya, dan versi yang dibatasi akan merusak banyak tipe Omit buatan sendiri yang sudah ada di berbagai proyek. Untuk kode sehari-hari, versi yang lebih ketat bisa menangkap salah ketik itu:
index.ts(10,36): error TS2344: Type '"pasword"' does not satisfy the constraint 'keyof User'.
index.ts(11,7): error TS2741: Property 'password' is missing in type '{ id: number; name: string; }' but required in type 'PublicUser'.
Error pertama adalah yang penting: key-nya dicek terhadap keyof User. Error kedua muncul akibat yang pertama, karena tidak ada yang di-omit dan PublicUser masih mewajibkan password. Perbaiki ejaannya menjadi "password" dan kodenya lolos kompilasi. Banyak codebase mendefinisikannya sekali di file tipe bersama dan memakainya sebagai pengganti Omit.
Mengganti Tipe Property
Sebuah interface tidak bisa meng-extend interface lain sambil mengubah property ke tipe yang tidak kompatibel: interface ApiUser extends User { id: string } gagal dengan error TS2430: Interface 'ApiUser' incorrectly extends interface 'User'. Omit property itu dulu, lalu deklarasikan lagi:
Dengan type alias, hal yang sama ditulis sebagai intersection: type ApiUser = Omit<User, "id"> & { id: string }. Tanpa Omit, intersection User & { id: string } akan memberi id tipe number & string, yaitu never.
Omit pada Tipe Union
Omit tidak bersifat distributif. Pada union, ia mulai dari keyof seluruh union, yang hanya berisi key yang dimiliki semua anggotanya, sehingga property milik masing-masing anggota hilang:
index.ts(8,39): error TS2353: Object literal may only specify known properties, and 'radius' does not exist in type 'NewShape'.
NewShape berubah menjadi { kind: "circle" | "square" }. Untuk meng-omit dari setiap anggota secara terpisah, terapkan Omit di dalam conditional type yang distributif:
T extends unknown ? ... : never selalu bernilai true; bagian ini ada hanya supaya conditional type terdistribusi ke setiap anggota union. Halaman conditional types menjelaskan kenapa cara ini berhasil. Pakai DistributiveOmit setiap kali tipe yang kamu omit adalah discriminated union.
Meng-omit Property Bersarang
Omit hanya melihat key di level teratas, jadi Omit<Settings, "editor.wordWrap"> tidak menghapus apa pun (dan, seperti yang ditunjukkan di atas, tidak mengeluh). Untuk membuang property satu level di bawahnya, omit property induknya lalu tambahkan lagi dengan Omit miliknya sendiri:
Settings["editor"] adalah indexed access type: ia menamai object type yang bersarang supaya Omit bisa bekerja padanya.
Menghapus Property Saat Runtime
Omit hanya mengubah tipe. Untuk menghasilkan object tanpa property tertentu, buang property itu lewat destructuring, atau tulis helper generic kecil yang tipe kembaliannya Omit:
Helper itu membatasi K ke keyof T, jadi key yang salah eja menjadi error di tempat pemanggilan. Salinannya dangkal: object bersarang tetap dipakai bersama dengan aslinya.
Omit vs Pick vs Exclude
| Bekerja pada | Mempertahankan | Contoh | Hasil | |
|---|---|---|---|---|
Omit<T, K> | object type | semua property kecuali K | Omit<User, "password"> | { id; name; email } |
Pick<T, K> | object type | hanya property di K | Pick<User, "id" | "name"> | { id; name } |
Exclude<U, M> | tipe union | anggota union yang tidak assignable ke M | Exclude<"a" | "b" | "c", "a"> | "b" | "c" |
Pilih antara Omit dan Pick berdasarkan apa yang seharusnya terjadi saat tipe sumbernya bertambah. Omit otomatis menyertakan property baru, cocok untuk "semuanya kecuali yang rahasia". Pick tidak pernah menyertakannya, cocok untuk "tepat field-field ini" dan lebih aman untuk apa pun yang keluar dari server-mu. Pick mengecek key-nya terhadap keyof T; Omit tidak.
Pertanyaan yang sering diajukan
Apa fungsi Omit di TypeScript?
Omit<T, K> membangun object type baru yang berisi semua property T kecuali property yang key-nya ada di K. Omit<User, "password"> adalah User tanpa password. Omit hanya mengubah tipe; ia tidak menghapus apa pun dari object saat runtime.
Bagaimana cara meng-omit beberapa property di TypeScript?
Berikan union dari beberapa key sebagai argumen kedua: Omit<User, "password" | "createdAt" | "updatedAt">. Setiap key di union itu akan dihapus.
Apa perbedaan Omit dan Exclude?
Omit menghapus property dari object type: Omit<{ a: 1; b: 2 }, "a"> adalah { b: 2 }. Exclude menghapus anggota dari tipe union: Exclude<"a" | "b", "a"> adalah "b". Omit diimplementasikan dengan Exclude: Pick<T, Exclude<keyof T, K>>.
Kenapa Omit tidak memberi error untuk key yang tidak ada?
Parameter key-nya dibatasi ke keyof any (string, number, atau symbol apa pun), bukan ke keyof T, jadi salah ketik seperti Omit<User, "pasword"> tetap lolos kompilasi dan tidak menghapus apa pun. Definisikan type StrictOmit<T, K extends keyof T> = Omit<T, K> supaya key yang tidak dikenal menghasilkan error.
Bagaimana cara menghapus property dari object saat runtime di TypeScript?
Pakai destructuring dengan rest element: const { password, ...rest } = user;. TypeScript memberi rest tipe object tanpa password, bentuk yang sama dengan Omit<User, "password">, dan property itu benar-benar hilang dari object baru.