TypeScript menyertakan kumpulan tipe pembantu yang disebut utility types, yaitu tipe yang membentuk tipe lain menjadi variasi baru. Enam yang paling sering digunakan adalah Partial, Required, Pick, Omit, Record, dan Readonly. Keenamnya dibahas di sini dalam skenario pengelolaan data sehari-hari. Semuanya bekerja murni pada level tipe, sehingga tidak ada satu pun yang meninggalkan jejak pada JavaScript hasil kompilasi.
Partial dan Required#
Partial<T> menjadikan seluruh properti T bersifat opsional, sedangkan Required<T> menjadikannya wajib. Pemakaian ini lazim untuk pola pembaruan data, ketika fungsi menerima potongan perubahan, bukan objek utuh.
interface Buku {
judul: string;
penulis: string;
halaman: number;
}
function perbaruiBuku(buku: Buku, perubahan: Partial<Buku>): Buku {
return { ...buku, ...perubahan };
}
const buku: Buku = { judul: "Kamus Mini", penulis: "Rani", halaman: 240 };
console.log(perbaruiBuku(buku, { halaman: 248 }));
interface Pengaturan {
tema?: string;
ukuran?: number;
}
const pengaturanLengkap: Required<Pengaturan> = { tema: "terang", ukuran: 14 };
console.log(pengaturanLengkap);{ judul: 'Kamus Mini', penulis: 'Rani', halaman: 248 }
{ tema: 'terang', ukuran: 14 }Fungsi perbaruiBuku menerima Partial<Buku>, sehingga pemanggil hanya mengirim properti yang berubah, yaitu halaman. Di dalamnya, spread { ...buku, ...perubahan } menggabungkan nilai lama dengan perubahan, dan hasilnya kembali bertipe Buku utuh. Baris kedua keluaran menunjukkan Required yang menuntut tema dan ukuran diisi, padahal keduanya opsional pada Pengaturan.
Kombinasi lain yang lazim adalah tipe draf untuk formulir bertahap, misalnya type DrafBuku = Partial<Buku> yang boleh diisi properti demi properti sebelum lengkap. Ketika pengisian selesai, keabsahannya dapat diperiksa terhadap tipe Buku utuh sebelum data diproses lebih lanjut. Pola ini menjaga definisi tetap tunggal, karena draf dan bentuk akhirnya berasal dari satu deklarasi yang sama.
Pick dan Omit#
Pick<T, K> memilih sebagian properti dari T, sedangkan Omit<T, K> membuang properti yang disebutkan. Keduanya berguna ketika tampilan yang berbeda membutuhkan kelompok properti yang berbeda dari satu sumber data yang sama.
interface CatatanPengeluaran {
tanggal: string;
uraian: string;
jumlah: number;
kategori: string;
}
type Ringkasan = Pick<CatatanPengeluaran, "tanggal" | "jumlah">;
type UntukPublik = Omit<CatatanPengeluaran, "uraian">;
const catatan: CatatanPengeluaran = {
tanggal: "2026-02-10",
uraian: "kertas A4",
jumlah: 45000,
kategori: "alat tulis",
};
const ringkas: Ringkasan = { tanggal: catatan.tanggal, jumlah: catatan.jumlah };
const publik: UntukPublik = {
tanggal: catatan.tanggal,
jumlah: catatan.jumlah,
kategori: catatan.kategori,
};
console.log(ringkas);
console.log(publik);{ tanggal: '2026-02-10', jumlah: 45000 }
{ tanggal: '2026-02-10', jumlah: 45000, kategori: 'alat tulis' }Tipe Ringkasan hanya memuat tanggal dan jumlah, sedangkan UntukPublik memuat semua kecuali uraian yang bersifat internal. Pemilihan lewat Pick lebih tegas, karena properti yang tidak disebut tidak akan ikut, sedangkan Omit lebih ringkas ketika properti yang dibuang lebih sedikit daripada yang dipertahankan. Keduanya menghasilkan tipe baru tanpa menduplikasi definisi CatatanPengeluaran.
Keenam utility types pada topik ini dapat dirangkai, dan rangkaian seperti Readonly<Omit<CatatanPengeluaran, "uraian">> bukan hal yang aneh. Hasilnya adalah tampilan publik yang sekaligus tidak dapat diubah. Keterbacaan tetap terjaga asalkan rangkaiannya disusun dari dalam ke luar dan dibaca sebagai satu kalimat tipe.
Pendekatan ini juga memudahkan pemeliharaan ketika interface induk berubah. Properti yang ditambahkan pada CatatanPengeluaran otomatis muncul pada UntukPublik, sedangkan Ringkasan tetap hanya berisi dua properti yang disebut. Dampak perubahan pun terlihat jelas, karena penambahan properti tidak mengubah tampilan yang memang tidak menyebutnya.
Record untuk kamus berkunci#
Record<K, V> membentuk tipe objek dengan kunci bertipe K dan nilai bertipe V. Kunci K dapat berupa union literal, sehingga kelengkapan kunci dapat diwajibkan oleh compiler.
type JenisTiket = "reguler" | "vip";
const stokTiket: Record<JenisTiket, number> = { reguler: 120, vip: 8 };
const frekuensiKata: Record<string, number> = { kata: 3, tipe: 1 };
console.log(stokTiket);
console.log(frekuensiKata);{ reguler: 120, vip: 8 }
{ kata: 3, tipe: 1 }// tidak-dijalankan
type JenisTiket = "reguler" | "vip";
const stokTiket: Record<JenisTiket, number> = { reguler: 120 };
console.log(stokTiket);# berkas ini sengaja tidak lolos pemeriksaan tipe
tsc --noEmit record-salah.tsrecord-salah.ts(4,7): error TS2741: Property 'vip' is missing in type '{ reguler: number; }' but required in type 'Record<JenisTiket, number>'.Objek stokTiket dideklarasikan dengan Record<JenisTiket, number>, sehingga kunci reguler dan vip wajib ada sekaligus. Versi yang kehilangan kunci vip ditolak compiler dengan menyebut properti yang kurang. Pada pemakaian dengan kunci bebas seperti frekuensiKata, Record<string, number> menjadi padanan index signature yang lebih ringkas.
Perbedaan perilaku kedua bentuk kunci itu penting dipahami. Record<JenisTiket, number> mewajibkan seluruh kunci union, sedangkan Record<string, number> menerima kunci apa pun tanpa jaminan kelengkapan. Karena itu, kunci literal memberi jaminan lebih kuat untuk data yang jenisnya sudah pasti, sedangkan kunci string lebih sesuai untuk data yang tidak diketahui sejak awal, misalnya Record<string, string[]> sebagai kamus dari kata ke daftar maknanya.
Readonly untuk data yang terkunci#
Readonly<T> menjadikan seluruh properti T bersifat readonly, sehingga penugasan ulang pada propertinya ditolak compiler. Tipe ini cocok untuk konfigurasi yang semestinya tetap selama program berjalan, misalnya jadwal piket yang sudah ditetapkan.
interface KonfigurasiJadwal {
hari: string;
jam: string;
}
const jadwal: Readonly<KonfigurasiJadwal> = { hari: "Senin", jam: "08.00" };
console.log(`${jadwal.hari} pukul ${jadwal.jam}`);Senin pukul 08.00// tidak-dijalankan
interface KonfigurasiJadwal {
hari: string;
jam: string;
}
const jadwal: Readonly<KonfigurasiJadwal> = { hari: "Senin", jam: "08.00" };
jadwal.hari = "Selasa";
console.log(jadwal.hari);# berkas ini sengaja tidak lolos pemeriksaan tipe
tsc --noEmit readonly-salah.tsreadonly-salah.ts(9,8): error TS2540: Cannot assign to 'hari' because it is a read-only property.Error di atas muncul pada baris penugasan, bukan pada deklarasi, karena yang diubah Readonly adalah aturan aksesnya, bukan nilainya. Pelarangan ini hanya berlaku saat kompilasi, sehingga objek yang sama tetap dapat diubah bila diteruskan lewat jalur yang tidak bertipe readonly. Untuk pelarangan yang ditegakkan saat runtime, Object.freeze dapat dipadukan dengan Readonly.
Pemakaian Readonly paling sesuai untuk data yang bersumber dari luar logika utama, misalnya berkas konfigurasi dan konstanta aplikasi. Penandaiannya sekaligus menjadi dokumentasi, karena pembaca tanda tangan langsung tahu bahwa fungsi tersebut tidak mengubah nilai yang diterimanya. Editor pun dapat menandai penugasan ulang semacam itu sebelum program dijalankan.
Kesalahan umum#
Kekeliruan pertama adalah menganggap utility types berjalan saat runtime. Seluruh tipe pada topik ini hilang bersama anotasi lain saat kompilasi, sehingga objek bertipe Readonly tetap dapat diubah oleh kode JavaScript yang mengabaikan tipenya. Perbaikannya adalah memadukan tipe dengan perlindungan runtime seperti Object.freeze bila integritas datanya benar-benar kritis.
Kekeliruan kedua adalah menganggap Partial<T> membuat properti bersarang ikut opsional. Utility types pada topik ini bekerja satu lapis, sehingga Partial pada objek yang berisi objek hanya membuat objek luarnya opsional, sedangkan properti di dalamnya tetap wajib. Perbaikannya adalah menulis sendiri tipe partial mendalam atau memperbarui tiap lapisan secara terpisah.
Kekeliruan ketiga adalah menganggap Readonly melindungi properti bersarang. Sifat readonly hanya menempel pada lapisan terluar, sehingga objek di dalam propertinya tetap dapat diubah melalui referensinya. Perbaikannya adalah menulis sendiri tipe readonly mendalam atau menerapkan Readonly pada tipe dalam secara eksplisit.
Ringkasan#
PartialdanRequiredmengubah kewajiban properti, dan keduanya menjadi dasar pola pembaruan data berbasis spread.PickdanOmitmembentuk tampilan tipe baru dari subset properti, denganPicklebih tegas danOmitlebih ringkas.Record<K, V>membentuk tipe kamus, termasuk mewajibkan kelengkapan kunci saatKberupa union literal.Readonlymelarang penugasan ulang properti saat kompilasi, sedangkan pelarangan runtime membutuhkanObject.freeze.
Referensi#
- Utility Types — The TypeScript Handbook — Dokumentasi resmi keenam tipe pembantu yang dibahas pada topik ini.