Utility type adalah tipe bawaan TypeScript yang membuat tipe baru dari tipe yang sudah ada. Kamu cukup menulis User sekali, lalu Partial<User>, Pick<User, ...>, atau Omit<User, ...> menurunkan versi lain darinya. Mirip memfotokopi formulir: dari satu formulir asli kamu bisa bikin salinan yang semua kolomnya boleh kosong, salinan yang cuma berisi dua kolom, atau salinan tanpa kolom tertentu. Formulir aslinya gak berubah.
🧪 Tebakan cepat: objek kosong
{}boleh disimpan sebagaiUser? Kalau sebagaiPartial<User>? Tebak dulu, jawabannya ada di bagian "Partial dan Required".
Semua contoh di bawah memakai interface ini:
interface User {
id: number;
nama: string;
email: string;
umur?: number; // opsional
}
Utility type ditulis dengan kurung siku lancip < >, sama seperti generic, karena memang generic: User dikirim sebagai type parameter-nya.
Partial dan Required
Partial<T> membuat semua properti opsional. Required<T> kebalikannya: semua properti jadi wajib, termasuk yang tadinya pakai ?.
// Partial<User> sama dengan { id?: number; nama?: string; email?: string; umur?: number }
const kosong: Partial<User> = {}; // lolos
const salah: User = {}; // error
error TS2739: Type '{}' is missing the following properties from type 'User': id, nama, email
{} lolos sebagai Partial<User> tapi ditolak sebagai User, karena id, nama, dan email wajib. Itu jawaban tebakannya. Partial cocok untuk data update yang cuma mengubah sebagian, misalnya { email: "[email protected]" }.
// Required<User> sama dengan { id: number; nama: string; email: string; umur: number }
const lengkap: Required<User> = { id: 1, nama: "Budi", email: "[email protected]" }; // error
error TS2741: Property 'umur' is missing in type '{ id: number; nama: string; email: string; }' but required in type 'Required<User>'.
Pick dan Omit
Pick<T, K> mengambil properti tertentu saja. Omit<T, K> kebalikannya: membuang properti tertentu dan menyimpan sisanya. K adalah union nama properti, seperti hasil keyof.
type KartuProfil = Pick<User, "nama" | "email">;
// sama dengan { nama: string; email: string }
type FormDaftar = Omit<User, "id">;
// sama dengan { nama: string; email: string; umur?: number }
const form: FormDaftar = { id: 1, nama: "Budi", email: "[email protected]" }; // error
error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Omit<User, "id">'.
idsudah dibuang olehOmit, jadi menulisnya dianggap properti yang gak dikenal.umurtetap opsional di hasilOmitdanPick. Utility ini gak mengubah tanda?.- Di pesan error, TypeScript menulis daftar kuncinya urut abjad, misalnya
Pick<User, "email" | "nama">.
Readonly dan Record
Readonly<T> membuat semua properti gak bisa diubah setelah objeknya dibuat:
const admin: Readonly<User> = { id: 1, nama: "Budi", email: "[email protected]" };
admin.nama = "Ani"; // error
error TS2540: Cannot assign to 'nama' because it is a read-only property.
Record<K, V> membuat object dengan kunci bertipe K dan nilai bertipe V:
type Nama = "budi" | "ani" | "rina";
const skor: Record<Nama, number> = { budi: 7, ani: 7, rina: 7 }; // ketiga nama wajib ada
const nilai: Record<string, number> = {}; // kunci teks apa pun, nilainya number
nilai["budi"] = 0;
nilai["ani"] = 0; // nilai sekarang { budi: 0, ani: 0 }
- Dengan
Kberupa union sepertiNama, tiap kunci wajib ada. Kalaurinagak ditulis:Property 'rina' is missing in type '{ budi: number; ani: number; }' but required in type 'Record<Nama, number>'. - Dengan
K=string, object-nya boleh kosong dan kuncinya bisa ditambah satu per satu. Untuk membuatnya dari array nama, mulai dari object kosong, lalu isiobj[nama] = nilaidi dalamfor...of.
⚠️ Jebakan yang sering ketemu
- Partial untuk data yang harus lengkap.
Partial<User>menerima{}, jadi data yang belum lengkap ikut lolos. Pakai Partial cuma untuk data yang memang boleh sebagian. - Salah ketik nama kunci di Omit.
Pick<User, "alamat">ditolak (Type '"alamat"' does not satisfy the constraint 'keyof User'.), tapiOmit<User, "idd">diam saja dan gak membuang apa pun. - Mengira Readonly berlaku saat program jalan.
readonlycuma dicek TypeScript. Setelah compile, JavaScript tetap bisa mengubah properti itu. - Lupa mengembalikan object yang sudah diisi. Mengisi
Recorddi dalam loop gak ada artinya kalau fungsinya gakreturnobject itu di akhir.
TL;DR: Utility type membuat tipe baru dari tipe yang ada. Partial<User> membuat semua properti opsional (jadi {} lolos), Required<User> membuat semuanya wajib, dan Readonly<User> membuatnya gak bisa diubah. Pick<User, "nama" | "email"> mengambil properti tertentu, Omit<User, "id"> membuangnya, kebalikan dari Pick. Record<K, V> adalah object dengan kunci K dan nilai V, misalnya Record<string, number> untuk skor per nama seperti { budi: 0, ani: 0 }.