Dalam pengembangan aplikasi skala besar menggunakan TypeScript, pengelolaan tipe data menjadi semakin kompleks. Ketika kita berurusan dengan banyak variasi struktur data, menulis tipe secara manual bisa menjadi pekerjaan yang melelahkan dan rentan kesalahan. Di sinilah Mapped Types hadir sebagai solusi efisien untuk membentuk tipe baru berdasarkan tipe yang sudah ada secara dinamis.
Selain itu, TypeScript juga memiliki fitur Conditional Types, yang memungkinkan kita membuat tipe yang dapat berubah tergantung pada kondisi tertentu. Dengan kombinasi Mapped Types dan Conditional Types, developer dapat menulis kode yang lebih fleksibel, kuat, dan mudah dipelihara — terutama untuk aplikasi berskala besar yang membutuhkan konsistensi antar tipe data.
Apa Itu Mapped Types?
Mapped Types adalah fitur TypeScript yang memungkinkan kita membuat tipe baru dengan memodifikasi tipe yang sudah ada menggunakan sintaks berbasis iterasi terhadap properti dari tipe tersebut. Dengan kata lain, kita bisa “memetakan” setiap properti dari tipe lama ke tipe baru dengan perubahan tertentu.
Contoh sederhana Mapped Types:
type User = {
name: string;
age: number;
active: boolean;
};
type ReadonlyUser = {
readonly [K in keyof User]: User[K];
};
Pada contoh di atas, ReadonlyUser adalah tipe baru yang dibentuk dari User, namun setiap propertinya ditandai sebagai readonly. Operator keyof digunakan untuk mengambil semua nama properti dari User, sedangkan in digunakan untuk melakukan iterasi terhadap properti tersebut.
Contoh Praktis Penggunaan Mapped Types
Mapped Types sering digunakan untuk membuat variasi tipe seperti berikut:
1. Readonly<T>
Menjadikan semua properti tipe T hanya bisa dibaca.
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
2. Partial<T>
Membuat semua properti menjadi opsional.
type Partial<T> = {
[K in keyof T]?: T[K];
};
3. Record<K, T>
Membuat tipe objek dengan kunci tertentu dan tipe nilai yang sama.
type Record<K extends string, T> = {
[P in K]: T;
};
type ProductCategory = Record<"food" | "drink" | "snack", number>;
Dengan Record, kita bisa membuat tipe dengan struktur kunci dan nilai yang fleksibel tanpa menulis setiap properti satu per satu.
Apa Itu Conditional Types?
Conditional Types memungkinkan kita mendefinisikan tipe berdasarkan kondisi logika. Secara sintaksis, ia mirip dengan pernyataan if dalam kode biasa, namun bekerja di level tipe. Struktur dasarnya adalah:
T extends U ? X : Y
Artinya: jika T merupakan subtype dari U, maka hasilnya adalah X, jika tidak maka Y.
Contoh penggunaan sederhana:
type IsString<T> = T extends string ? "ya, string" : "bukan string"; type A = IsString<string>; // "ya, string" type B = IsString<number>; // "bukan string"
Conditional Types memungkinkan kita membangun sistem tipe yang cerdas dan adaptif, tergantung pada input tipe yang diberikan.
Contoh Penggunaan Conditional Types dalam Aplikasi
Berikut contoh penerapan Conditional Types dalam konteks nyata:
1. Membuat tipe respons API dinamis
type ApiResponse<T> = T extends Error
? { success: false; message: string }
: { success: true; data: T };
type SuccessResponse = ApiResponse<{ name: string }>;
type ErrorResponse = ApiResponse<Error>;
Pada contoh di atas, jika tipe T merupakan Error, maka struktur respons otomatis menyesuaikan menjadi pesan error. Jika bukan, maka hasilnya berupa data sukses.
2. Membatasi tipe tertentu
type NonNullable<T> = T extends null | undefined ? never : T; type CleanType = NonNullable<string | null | undefined>; // Hasilnya hanya string
Dengan NonNullable, kita bisa membersihkan tipe dari nilai null atau undefined secara otomatis.
Kombinasi Mapped Types dan Conditional Types
Kedua fitur ini bisa digabungkan untuk membuat tipe yang lebih dinamis. Misalnya, kita bisa membuat tipe yang mengubah semua properti string menjadi number:
type ConvertStringToNumber<T> = {
[K in keyof T]: T[K] extends string ? number : T[K];
};
interface Employee {
id: number;
name: string;
salary: number;
}
type EmployeeConverted = ConvertStringToNumber<Employee>;
/*
Hasil:
{
id: number;
name: number;
salary: number;
}
*/
Dengan kombinasi ini, kita bisa memanipulasi tipe dengan logika yang sangat fleksibel tanpa menulis ulang banyak kode.
Kapan Menggunakan Mapped Types dan Conditional Types
Gunakan Mapped Types ketika:
-
Anda ingin membuat variasi tipe dari tipe yang sudah ada (misal membuat semua properti opsional, readonly, atau nullable).
-
Anda ingin menyesuaikan struktur tipe tanpa mengulang deklarasi aslinya.
Gunakan Conditional Types ketika:
-
Anda perlu membuat tipe yang bergantung pada kondisi tertentu.
-
Anda ingin menyesuaikan hasil tipe berdasarkan hubungan antar-tipe (misalnya, hasil API, pengecekan null, atau validasi generik).
Kesimpulan
Mapped Types dan Conditional Types adalah dua fitur canggih dalam TypeScript yang memungkinkan developer membuat sistem tipe yang dinamis, efisien, dan mudah dikelola. Mapped Types membantu menciptakan tipe baru dari tipe yang sudah ada, sedangkan Conditional Types memberikan fleksibilitas untuk membuat tipe yang bisa menyesuaikan diri berdasarkan kondisi.
Dengan memahami keduanya, Anda dapat menulis kode TypeScript yang lebih aman, konsisten, dan scalable — sangat penting untuk proyek besar dan tim pengembangan profesional.