September 28, 2026
Generics & Utility Types.
Membuat tipe yang reusable di TypeScript: generics dan constraint, keyof & typeof, indexed access, mapped types, conditional types, serta utility types bawaan seperti Partial, Pick, Omit, Record, dan ReturnType.
Setelah paham dasar TypeScript , langkah berikutnya adalah membuat tipe yang tidak diketik ulang. Generics membuat fungsi dan komponen bekerja untuk banyak tipe sekaligus, sedangkan utility types dan mapped types memungkinkan kita menurunkan tipe baru dari tipe yang sudah ada β satu sumber kebenaran, banyak turunan.
1. Generics
Tanpa generics, kita harus memilih antara tipe spesifik (tidak reusable) atau any (tidak aman):
function pertamaAny(arr: any[]): any {
return arr[0];
}
const x = pertamaAny([1, 2, 3]); // x: any π
Dengan generics, tipe menjadi parameter:
function pertama<T>(arr: T[]): T | undefined {
return arr[0];
}
const a = pertama([1, 2, 3]); // a: number | undefined
const b = pertama(["x", "y"]); // b: string | undefined
const c = pertama<boolean>([true]); // eksplisit (jarang perlu)
T diinfer otomatis dari argumen. Namanya bebas; konvensinya T, K (key), V (value), E (element).
Generic pada type & interface
interface ApiResponse<T> {
data: T;
meta: { page: number; total: number };
}
type User = { id: number; nama: string };
async function getUsers(): Promise<ApiResponse<User[]>> {
const res = await fetch("/api/users");
return res.json();
}
Pola ini sangat umum saat mengambil data dari API : satu tipe wrapper untuk semua endpoint.
Constraint dengan extends
Batasi T agar punya properti tertentu:
function panjang<T extends { length: number }>(x: T): number {
return x.length;
}
panjang("halo"); // β
panjang([1, 2, 3]); // β
// panjang(42); // β number tidak punya length
Default type parameter
type Paginated<T = unknown> = { items: T[]; next: string | null };
const p: Paginated = { items: [], next: null }; // T = unknown
2. keyof, typeof, dan Indexed Access
Tiga operator ini adalah “bahan baku” untuk menurunkan tipe.
type Produk = { id: number; nama: string; harga: number };
type KunciProduk = keyof Produk; // "id" | "nama" | "harga"
type Harga = Produk["harga"]; // number (indexed access)
type NilaiProduk = Produk[keyof Produk]; // number | string
typeof di posisi tipe mengambil tipe dari nilai:
const config = {
apiUrl: "https://api.example.com",
timeout: 5000,
retry: true,
};
type Config = typeof config;
// { apiUrl: string; timeout: number; retry: boolean }
Kombinasi klasik β fungsi getter yang aman:
function ambil<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const p: Produk = { id: 1, nama: "Jam", harga: 250000 };
const h = ambil(p, "harga"); // h: number
// ambil(p, "stok"); // β "stok" bukan key Produk
as const untuk turunan dari array
const ROLES = ["admin", "editor", "viewer"] as const;
type Role = (typeof ROLES)[number]; // "admin" | "editor" | "viewer"
function isRole(x: string): x is Role {
return (ROLES as readonly string[]).includes(x);
}
Array runtime dan tipe union kini selalu sinkron β tambah role cukup di satu tempat.
3. Utility Types Bawaan
TypeScript menyediakan utility types global. Yang paling sering dipakai:
| Utility | Hasil | Contoh kasus |
|---|---|---|
Partial<T> | Semua properti opsional | Payload update (PATCH) |
Required<T> | Semua properti wajib | Konfigurasi setelah diisi default |
Readonly<T> | Semua properti read-only | State immutable |
Pick<T, K> | Ambil sebagian key | DTO publik |
Omit<T, K> | Buang sebagian key | Form create (tanpa id) |
Record<K, V> | Objek dengan key K bernilai V | Lookup table / map |
Exclude<U, M> | Buang anggota union | Status tanpa "archived" |
Extract<U, M> | Ambil anggota union yang cocok | Filter union |
NonNullable<T> | Buang null & undefined | Setelah validasi |
ReturnType<F> | Tipe return fungsi | Tipe dari fungsi pihak ketiga |
Parameters<F> | Tuple parameter fungsi | Wrapper/decorator |
Awaited<T> | Isi dari Promise | Hasil async function |
Contoh nyata dari satu model:
interface Artikel {
id: number;
judul: string;
isi: string;
status: "draft" | "published" | "archived";
createdAt: Date;
}
type ArtikelBaru = Omit<Artikel, "id" | "createdAt">;
type ArtikelUpdate = Partial<ArtikelBaru>;
type ArtikelRingkas = Pick<Artikel, "id" | "judul">;
type StatusAktif = Exclude<Artikel["status"], "archived">; // "draft" | "published"
const labelStatus: Record<Artikel["status"], string> = {
draft: "Draf",
published: "Terbit",
archived: "Arsip",
};
Jika nanti status bertambah "scheduled", labelStatus langsung error karena key-nya belum lengkap β Record dengan key union memaksa kelengkapan.
ReturnType dan Awaited berguna saat tipe hanya tersedia lewat fungsi:
async function fetchProfil() {
return { id: 1, nama: "Fanny", skills: ["PHP", "TS"] };
}
type Profil = Awaited<ReturnType<typeof fetchProfil>>;
// { id: number; nama: string; skills: string[] }
4. Mapped Types
Utility types di atas sebenarnya dibangun dengan mapped types: iterasi atas key sebuah tipe.
type OpsionalSemua<T> = {
[K in keyof T]?: T[K];
};
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};
Modifier readonly dan ? bisa ditambah (+) atau dihapus (-):
type Mutable<T> = {
-readonly [K in keyof T]: T[K];
};
Key remapping dengan as
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type User = { nama: string; umur: number };
type UserGetters = Getters<User>;
// { getNama: () => string; getUmur: () => number }
Template literal type (`get${...}`) dan intrinsic Capitalize, Uppercase, Lowercase, Uncapitalize membuat transformasi nama key jadi mudah.
5. Conditional Types (Sekilas)
Bentuknya T extends U ? X : Y β seperti ternary untuk tipe:
type IsString<T> = T extends string ? true : false;
type A = IsString<"halo">; // true
type B = IsString<42>; // false
// infer: "ekstrak" bagian dari tipe
type ElemenArray<T> = T extends (infer E)[] ? E : never;
type C = ElemenArray<number[]>; // number
Conditional type bersifat distributive terhadap union: IsString<string | number> menjadi boolean (true | false). Untuk kebanyakan kode aplikasi, cukup tahu bahwa ini yang membuat Exclude, Extract, dan ReturnType bekerja β jarang perlu menulis sendiri.
Kesalahan Umum
- Generic yang tidak perlu.
function f<T>(x: T): voidtanpa memakaiTdi tempat lain hanya menambah noise. Generic berguna jika menghubungkan input dengan output. - Constraint terlupa. Mengakses
x.lengthpadaTtanpaextends { length: number }akan error. - Menduplikasi tipe secara manual. Menulis ulang
ArtikelBarudengan tangan membuatnya cepat basi; turunkan denganOmit/Pick. Omitdengan key salah ketik.Omit<T, "judull">tidak error (key-nya bertipestringbebas). Periksa hasilnya dengan hover di editor.- Lupa
as const. Tanpa itu,["admin", "editor"]bertipestring[]dan union turunan menjadistring. - Conditional type yang terlalu “pintar”. Tipe yang sulit dibaca sama buruknya dengan kode yang sulit dibaca β utamakan kejelasan.
Partialuntuk semua hal. Membuat semua properti opsional di mana-mana justru menghilangkan jaminan; pakai hanya di batas yang memang parsial (update, draft form).
Ringkasan
- Generics = parameter tipe; berguna saat menghubungkan tipe input dan output.
keyof,typeof, danT[K]adalah alat untuk menurunkan tipe dari yang sudah ada.- Utility types (
Partial,Pick,Omit,Record,ReturnType,Awaited) menutup 90% kebutuhan sehari-hari. - Mapped & conditional types adalah fondasi di balik utility types.
- Lanjut ke TypeScript di Proyek Nyata
untuk konfigurasi
tsconfigdan integrasi CI.

Hey! I’m Fanny, the software engineer tending to this digital garden. You can read more about me, or subscribe by email.