Generic adalah parameter tipe. Fungsi generic mendeklarasikan sebuah tipe placeholder, biasanya bernama T, dan setiap pemanggilan mengisinya, jadi tipe yang masuk menentukan tipe yang keluar. Satu fungsi lalu bisa bekerja untuk angka, string, dan object buatanmu sendiri, dan setiap pemanggilan tetap bertipe lengkap.
<T> setelah nama fungsi mendeklarasikan parameter tipe. items: T[] memakainya untuk input, dan T | undefined untuk output, sehingga keduanya terikat satu sama lain. Kamu tidak pernah menulis number atau string: TypeScript menyimpulkan T dari argumennya.
Kenapa Tidak Pakai any atau unknown?
Tanpa generic, kamu akan memberi tipe parameter any atau unknown. Keduanya menerima semua array, dan keduanya kehilangan hubungan antara input dan output:
| Tipe parameter | Menerima semua input | Tipe hasil | Diperiksa |
|---|---|---|---|
any | ya | any | tidak: kesalahan tetap lolos kompilasi |
unknown | ya | unknown | ya, tetapi kamu harus melakukan narrowing sebelum memakainya |
T (generic) | ya | terikat pada input | ya, dengan tipe yang tepat |
Inferensi Argumen Tipe
Biasanya TypeScript menyimpulkan argumen tipe dari nilai yang kamu kirim. Kamu juga bisa menuliskannya secara eksplisit di dalam kurung sudut saat pemanggilan. Ini diperlukan kalau tidak ada argumen yang menyebut T, atau kalau hasil inferensinya bukan yang kamu inginkan.
Ada dua hasil inferensi yang perlu diketahui. pair(1, "one") tanpa argumen eksplisit adalah error: TypeScript menyimpulkan T = number dari argumen pertama, lalu melaporkan Argument of type 'string' is not assignable to parameter of type 'number'. (TS2345). Dan emptyList() tanpa argumen sama sekali mendapat T = unknown, sehingga hasilnya unknown[], yang jarang sesuai keinginanmu.
Beberapa Parameter Tipe
Sebuah fungsi bisa mendeklarasikan parameter tipe sebanyak yang dibutuhkan, dipisahkan dengan koma. Masing-masing disimpulkan secara terpisah.
mapValues menyimpulkan tiga hal sekaligus: K adalah "tea" | "cake", V adalah number, dan R adalah string dari nilai kembalian callback. K extends string adalah sebuah constraint, yang dibahas di halaman berikutnya.
Interface dan Type Alias Generic
Tipe juga bisa menerima parameter. Kamu lalu menulis argumennya saat memakai tipe itu: Box<number>, ApiResponse<User>. Sebagian besar tipe koleksi bawaan bekerja dengan cara ini: Array<T>, Map<K, V>, Promise<T>, Record<K, V>.
Result<T, E = string> juga menunjukkan parameter tipe default: Result<number> berarti Result<number, string>. Sama seperti parameter fungsi opsional, parameter yang punya default harus ditulis setelah parameter yang wajib.
Class Generic
Class menerima parameter tipe setelah namanya, dan setiap instance menetapkannya. Field, method, dan parameter constructor semuanya bisa memakainya.
Kalau constructor menerima T, kamu bisa menghilangkan argumen eksplisitnya: new Box(5) menyimpulkan Box<number>. Member static milik class itu sendiri, bukan milik instance, jadi tidak bisa memakai parameter tipe class (Static members cannot reference class type parameters., TS2302).
Arrow Function dan Tipe Fungsi Generic
Daftar parameter tipe ditulis sebelum daftar parameter. Sintaks yang sama juga dipakai untuk menjelaskan tipe fungsi generic.
const last = <T>(items: T[]): T | undefined => items[items.length - 1];
type Mapper = <T, R>(items: T[], fn: (item: T) => R) => R[];
const mapAll: Mapper = (items, fn) => items.map(fn);
// In a .tsx file, <T> looks like a JSX tag. Add a trailing comma:
const lastTsx = <T,>(items: T[]) => items[items.length - 1];
Menyimpulkan Tipe Literal dengan Parameter Tipe const
Secara default, generic menyimpulkan tipe yang dilebarkan: pair("a", "b") menghasilkan [string, string]. Kalau nilai yang persis itu penting (nama route, nama event, daftar kolom), tandai parameternya dengan const (TypeScript 5.0 ke atas), dan inferensinya akan seolah-olah argumennya ditulis as const:
Pemanggil menulis array biasa, dan fungsinya mempertahankan tipe literalnya.
Kapan Tidak Memakai Generic
Parameter tipe layak dipakai kalau ia menghubungkan dua hal: sebuah parameter dan tipe kembalian, dua parameter, atau sebuah parameter dan callback. Kalau T hanya muncul sekali, ia tidak menambah apa pun, dan tipe biasa lebih jelas.
// Pointless: T is used once, so it is just a longer way to write unknown
function logValue<T>(value: T): void {
console.log(value);
}
// Clearer
function logValueSimple(value: unknown): void {
console.log(value);
}
// Also pointless: returns T but nothing connects T to an argument,
// so the caller is really just asserting a type
function parseJson<T>(text: string): T {
return JSON.parse(text);
}
Contoh terakhir sering ditemui dan menyesatkan: parseJson<User>(text) terlihat type-safe, tetapi tidak memeriksa apa pun, persis seperti JSON.parse(text) as User. Kembalikan unknown lalu lakukan validasi. Tanda lain penggunaan berlebihan: parameter tipe yang selalu diberi argumen yang sama, atau yang bisa diganti dengan union dari dua tipe yang sudah diketahui.
Untuk generic yang perlu tahu sesuatu tentang T (bahwa ia punya length, atau bahwa K adalah key dari T), constraint dengan extends adalah langkah selanjutnya.
Pertanyaan yang sering diajukan
Apa itu generic di TypeScript?
Generic adalah parameter tipe: placeholder seperti T yang diisi dengan tipe sungguhan setiap kali fungsi, interface, type alias, atau class dipakai. function first<T>(items: T[]): T | undefined bekerja untuk array apa pun, dan hasilnya bertipe elemen dari array yang kamu kirim, jadi first([1, 2]) bertipe number | undefined dan first(["a"]) bertipe string | undefined.
Apa perbedaan generic dan any?
any mematikan pemeriksaan tipe: nilai yang masuk dan nilai yang keluar tidak punya hubungan, dan hasilnya juga any. Generic mempertahankan hubungan itu: identity<T>(x: T): T mengembalikan tipe yang persis sama dengan yang kamu kirim. Pakai unknown kalau kamu menerima apa saja tetapi tidak perlu mengembalikan tipenya, dan pakai generic kalau perlu.
Apa arti <T> di TypeScript?
Itu mendeklarasikan parameter tipe bernama T. Di function wrap<T>(value: T), T adalah variabel tipe yang diisi TypeScript dari argumen di setiap pemanggilan, atau yang kamu kirim secara eksplisit seperti wrap<string>("a"). Nama T hanyalah konvensi; nama yang lebih panjang seperti TItem atau Key juga bekerja dengan cara yang sama.
Bagaimana cara menulis arrow function generic di TypeScript?
Letakkan daftar parameter tipe sebelum parameternya: const first = <T>(items: T[]): T | undefined => items[0];. Di file .tsx, <T> akan dibaca sebagai tag JSX, jadi tulis <T,> dengan koma di belakangnya, atau <T extends unknown>.
Bagaimana cara memberi tipe default pada generic di TypeScript?
Tambahkan = Type setelah parameternya: interface ApiResponse<T = unknown> { data: T }. Maka ApiResponse tanpa argumen berarti ApiResponse<unknown>. Parameter yang punya default harus ditulis setelah parameter yang tidak punya default, sama seperti parameter fungsi opsional.