Di episode #106 kamu belajar memberi nama pada bentuk data lewat interface dan type. Sekarang kita naik satu level: function yang bisa bekerja untuk tipe apa pun tanpa kehilangan keamanan tipe. Ini namanya generics, dan ini adalah trik yang menghentikan kebiasaan buruk menulis function yang sama berulang-ulang untuk tiap tipe data.
Kenapa penting? Karena masalah ini muncul di hampir setiap proyek: kamu butuh function ambilPertama untuk array angka, lalu butuh yang sama untuk array string, lalu untuk array user. Tanpa generics, pilihanmu cuma dua: duplikasi function tiga kali, atau pakai any dan membuang semua perlindungan TypeScript. Generics memberi pilihan ketiga: satu function, semua tipe, tetap aman. Di episode #108 nanti kita akan melihat generics beraksi di dunia nyata saat menggabungkan TypeScript dengan React.
Cara Berpikirnya: Cetakan, Bukan Fotokopi
Bayangkan tukang kue punya satu cetakan berbentuk bintang. Adonannya bisa cokelat, keju, atau pandan, tapi hasil akhirnya selalu berbentuk bintang. Cetakannya satu, variasinya di bahan yang dimasukkan.
Generic function bekerja persis seperti itu. Cetakannya adalah logika function-nya, bahannya adalah tipe data yang kamu masukkan:
function identitas<T>(nilai: T): T {
return nilai;
}
const a = identitas<string>("halo");
const b = identitas(42);
Perhatikan dua cara pemakaian. identitas<string> menyebut tipe secara eksplisit, seperti memberi tahu tukang kue "pakai adonan cokelat". Sedangkan identitas(42) membiarkan TypeScript menebak sendiri (inference): karena argumennya 42, ia menyimpulkan T = number. Dalam praktik, inference dipakai jauh lebih sering karena kodenya lebih bersih. Aturan mainnya: tulis eksplisit hanya kalau inference gagal menebak atau kamu ingin memaksa tipe tertentu.
Kenapa tidak pakai any saja? Bandingkan:
function identitasAny(nilai: any): any {
return nilai;
}
const hasil = identitasAny("halo");
hasil.toUpperCase(); // lolos, tapi...
Berhenti dan tebak dulu! Apa beda hasil typeof dari dua baris ini menurut TypeScript?
const x = identitas("halo"); // generic
const y = identitasAny("halo"); // any
Dua Persepsi Salah yang Perlu Diluruskan
Mitos 1: "Generics itu fitur advanced, pemula tidak perlu." Justru sebaliknya: pemula paling sering jadi korban tidak adanya generics. Tanpa generics, pemula menulis tiga versi function yang sama atau menyerah pakai any, lalu bingung kenapa TypeScript "tidak membantu apa-apa". Generics dasar (satu parameter <T>) bisa dipahami dalam sepuluh menit dan langsung menghemat duplikasi di proyek pertamamu.
Mitos 2: "Huruf T itu wajib, ada aturan khususnya." Tidak. T cuma konvensi (singkatan dari Type). Kamu boleh pakai nama apa pun: <Data>, <Item>. Konvensinya: T untuk satu parameter, K dan V untuk key-value, atau nama deskriptif kalau genericnya kompleks. Yang penting konsisten dan bisa dibaca manusia.
Kasus Nyata: Function yang Bekerja untuk Semua Array
Ini pola yang akan kamu temui berkali-kali. Kamu butuh function yang mengambil elemen pertama dari sebuah array, apa pun isinya:
function ambilPertama<T>(daftar: T[]): T | undefined {
return daftar[0];
}
const angka = ambilPertama([10, 20, 30]); // number | undefined
const nama = ambilPertama(["sari", "budi"]); // string | undefined
Alur berpikirnya: T[] artinya "array berisi T", dan return type T | undefined jujur mengakui bahwa array bisa kosong. Kenapa return-nya tidak sekadar T? Karena daftar[0] pada array kosong menghasilkan undefined. Generic yang baik tidak berbohong tentang kemungkinan gagal. Tipe yang jujur memaksa pemakainya menangani kasus kosong, dan itu mencegah satu kelas bug yang sangat umum.
Constraint: Generic yang Punya Syarat
Kadang fleksibilitas penuh terlalu bebas. Misal kamu bikin function yang butuh properti length dari argumennya:
function panjang<T>(nilai: T): number {
return nilai.length; // ERROR: T belum tentu punya .length
}
Solusinya adalah constraint dengan kata kunci extends: T boleh apa saja, asal punya length.
function panjang<T extends { length: number }>(nilai: T): number {
return nilai.length; // sekarang aman
}
panjang("halo"); // 4, string punya length
panjang([1, 2, 3]); // 3, array punya length
panjang(42); // ERROR, number tidak punya length
Analoginya seperti lowongan kerja: "dibuka untuk semua jurusan, asal bisa berbahasa Inggris". Constraint tidak membatasi siapa yang boleh melamar secara sempit, ia hanya menetapkan syarat minimal agar function bisa bekerja dengan benar. Pola ini muncul terus di library nyata, misalnya function yang menerima objek apa pun asal punya id.
Satu catatan penting: jangan over-engineering generics. Kalau function-mu hanya dipakai untuk satu tipe, tulis tipenya langsung, jangan dipaksa generic "buat jaga-jaga". Generics adalah jawaban untuk masalah duplikasi yang nyata, bukan asuransi untuk masalah yang belum ada. Kode yang sederhana dan spesifik selalu lebih mudah dibaca daripada generic yang spekulatif. Aturan praktisnya: ekstrak menjadi generic saat kamu menulis function yang sama untuk kedua kalinya, bukan sebelumnya.
Kuis cepat: Apa tipe hasil pada kode ini?
function bungkus<T>(nilai: T): { data: T } {
return { data: nilai };
}
const hasil = bungkus(100);
Generics Bertemu Interface: ApiResponse
Ini pola paling sering dipakai di dunia kerja. Setiap respons API punya struktur yang sama (status sukses + data), tapi isi datanya beda-beda:
interface ApiResponse<T> {
sukses: boolean;
data: T;
pesan?: string;
}
interface User {
id: number;
nama: string;
}
const resUser: ApiResponse<User> = {
sukses: true,
data: { id: 1, nama: "Sari" }
};
const resAngka: ApiResponse<number[]> = {
sukses: true,
data: [1, 2, 3]
};
Kenapa ini elegan? Tanpa generics kamu harus bikin UserResponse, NumberListResponse, ProductResponse, dan seterusnya, semuanya dengan struktur yang 90 persen sama. Dengan satu interface generic, struktur didefinisikan sekali, isinya bervariasi. Ini adalah penerapan langsung pelajaran episode #106: interface untuk bentuk, generics untuk variasi.
Latihan: Bukan Mengetik Ulang
- Buat generic function
terakhir<T>(daftar: T[]): T | undefinedyang mengembalikan elemen terakhir array. Uji dengan array string dan array number, perhatikan tipe hasilnya berubah mengikuti input. - Ubah: tambahkan constraint sehingga function-mu hanya menerima array yang elemennya punya properti
id: number. Coba panggil dengan array string dan amati errornya. Lalu panggil dengan array objek{id, nama}dan pastikan lolos. - Tambah: buat interface generic
Pasangan<K, V>dengan propertikunci: Kdannilai: V. Buat functionbuatPasanganyang mengembalikanPasangandari dua argumen apa pun, lalu pakai untuk pasangan(1, "satu")dan("aktif", true). - Tantangan: ubah function
bungkusdari kuis di atas menjadibungkusan<T>(nilai: T, label: string)yang mengembalikan{ label: string, data: T }. Tebak dulu tipe hasilnya sebelum kamu hover di editor.
Di episode #108 kita masuk ke wilayah yang paling bikin penasaran: TypeScript ketemu React. Kamu akan melihat cara mengetik props component, kenapa useState butuh generic, dan bagaimana interface yang kamu pelajari di #106 menjadi bahasa sehari-hari component React. Kalau selama ini React-mu masih JavaScript polos, episode berikutnya adalah jembatan yang kamu tunggu.
Diskusi & Komentar 0
Punya tanggapan, pertanyaan, atau tips seputar tutorial ini? Tulis komentar kamu di bawah!
Jadilah yang pertama membuka obrolan dan berbagi pandangan seputar tutorial resep kode ini.
Tulis Komentar
Komentar menggunakan akun Google. Harap berkomentar dengan santun dan berbobot.