Generics Dasar TS
Tingkatkan fleksibilitas dan keamanan tipe kodemu! Pelajari konsep dasar Generics di TypeScript untuk membuat fungsi, interface, atau class yang bisa bekerja dengan berbagai tipe data secara aman.
Kode Jadi "Bunglon" Cerdas: Kenalan Sama Generics di TypeScript!
Udah mulai jago kan ngasih tipe ke data dan fungsi pake TypeScript? Keren! Tapi, kadang kita nemu situasi di mana kita pengen bikin fungsi atau struktur data yang bisa kerja sama berbagai macam tipe data, tapi kita tetep pengen dapet keamanan tipe (type safety) dari TypeScript.
Misalnya, kamu mau bikin fungsi yang tugasnya simpel: nerima satu argumen, terus ngembaliin argumen itu apa adanya (fungsi identitas). Kalau di JavaScript biasa:
function identitasJS(arg) {
return arg;
}
let outputJS = identitasJS("halo"); // outputJS tipenya any
let outputLainJS = identitasJS(123); // outputLainJS juga any
Masalahnya, kita jadi kehilangan informasi tipe. outputJS
dan outputLainJS
jadi bertipe any
, padahal kita tau inputnya string atau number.
Gimana caranya biar fungsi identitas
ini bisa nerima tipe apa aja, tapi tipe outputnya sama persis kayak tipe inputnya, dan TypeScript tetep bisa ngecek? Nah, di sinilah Generics datang sebagai pahlawan!
Apa Itu Generics? Kode yang Bisa Beradaptasi Sama Tipe!
Generics di TypeScript (dan di banyak bahasa pemrograman lain yang punya tipe statis) adalah cara buat nulis kode yang reusable dan bisa kerja dengan berbagai tipe data, sambil tetep ngejaga informasi tipe dan type safety.
Bayangin Generics itu kayak kamu bikin "cetakan" atau "template" buat fungsi atau tipe data, tapi ada beberapa bagian dari "cetakan" itu yang tipenya belum ditentuin pas kamu bikin cetakannya. Tipe itu baru bakal ditentuin (atau ditebak sama TypeScript) pas kamu pake cetakan itu.
Kita pake type variable (variabel tipe), biasanya ditulis pake satu huruf besar (konvensinya T
buat Type, E
buat Element, K
buat Key, V
buat Value, tapi bisa apa aja), yang diapit kurung sudut < >
. Variabel tipe ini yang jadi "placeholder" buat tipe data yang bakal dipake nanti.
Generics pada Fungsi: Bikin Fungsi "Satu untuk Semua" (Tipe)
Cara paling umum liat generics adalah di fungsi.
- Sintaks Dasar Fungsi Generic:
typescript function namaFungsiGeneric<T>(argumen: T): T { // Di dalem sini, 'T' bisa dianggap sebagai tipe data apa pun // yang dikasih pas fungsi ini dipanggil. // Return type-nya juga T, jadi sama kayak tipe argumen. return argumen; }
<T>
setelah nama fungsi: Ini yang ngedeklarasiin kalau fungsi ini adalah fungsi generic dengan satu type variable bernamaT
.argumen: T
: Parameterargumen
tipenyaT
.: T
setelah kurung parameter: Return type fungsi ini jugaT
.
Contoh Fungsi Identitas Pake Generic:
function identitas<T>(arg: T): T {
console.log("Tipe argumennya adalah:", typeof arg);
return arg;
}
// Cara pake fungsi generic:
// 1. TypeScript bisa nebak (infer) tipe T dari argumen yang dikasih:
let outputString = identitas("Halo TypeScript!"); // T ditebak jadi string
console.log(outputString.toUpperCase()); // Oke, karena outputString itu string
let outputNumber = identitas(12345); // T ditebak jadi number
console.log(outputNumber.toFixed(2)); // Oke, karena outputNumber itu number
let outputBoolean = identitas(true); // T ditebak jadi boolean
// 2. Atau, kamu bisa juga nulis tipe T-nya secara eksplisit pas manggil (jarang perlu kalau bisa di-infer):
let outputTanggal = identitas<Date>(new Date()); // Eksplisit bilang T itu Date
console.log(outputTanggal.getFullYear()); // Oke
Lihat bedanya sama contoh JavaScript biasa tadi? Sekarang, outputString
itu tipenya string
, outputNumber
tipenya number
, dst. TypeScript jadi tau tipe pastinya dan bisa ngasih auto-completion atau ngedeteksi error kalau kita salah pake metodenya.
Contoh Lain: Fungsi yang Ngambil Elemen Pertama dari Array (Tipe Apapun)
function ambilElemenPertama<TipeElemen>(arr: TipeElemen[]): TipeElemen | undefined {
return arr.length > 0 ? arr[0] : undefined;
}
let angkaPertama = ambilElemenPertama([10, 20, 30]); // TipeElemen jadi number, angkaPertama jadi number | undefined
console.log(angkaPertama); // Output: 10
let namaPertama = ambilElemenPertama(["Adi", "Budi"]); // TipeElemen jadi string, namaPertama jadi string | undefined
console.log(namaPertama); // Output: Adi
let kosong = ambilElemenPertama([]); // TipeElemen bisa apa aja, kosong jadi undefined
console.log(kosong); // Output: undefined
Di sini, TipeElemen
jadi placeholder buat tipe elemen di dalem array. Fungsi ini jadi bisa dipake buat array angka, array string, atau array apa aja, dan return type-nya bakal ngikutin tipe elemen array itu (plus undefined
kalau array-nya kosong).
Generics pada Interface dan Type Aliases: "Cetakan Bentuk" yang Fleksibel
Kamu juga bisa pake generics pas ngedefinisiin interface
atau type
alias buat bikin struktur data yang tipe salah satu propertinya bisa macem-macem.
Contoh Interface Generic buat "Kotak Pembungkus":
interface Kotak<TipeIsi> {
isi: TipeIsi;
bukaKotak: () => TipeIsi;
tambahLabel: (label: string) => void;
}
// Bikin Kotak yang isinya string
let kotakSurat: Kotak<string> = {
isi: "Surat penting!",
bukaKotak: function() { return this.isi; },
tambahLabel: (label) => console.log(`Label ditambahkan: ${label}`)
};
console.log(kotakSurat.bukaKotak().toUpperCase()); // Oke, karena isinya string
// Bikin Kotak yang isinya number
let kotakAngka: Kotak<number> = {
isi: 123,
bukaKotak: function() { return this.isi; },
tambahLabel: (label) => console.log(`Label angka: ${label}`)
};
console.log(kotakAngka.bukaKotak().toFixed(2)); // Oke, karena isinya number
// let kotakSalah: Kotak<number> = { isi: "salah tipe" }; // ERROR!
Dengan Kotak<TipeIsi>
, kita bisa bikin "Kotak" yang bisa nampung tipe apa aja, tapi tipe "isi"-nya itu konsisten dan type-safe.
Generics pada Class (Pengenalan Singkat)
Class di TypeScript juga bisa generic. Ini sering dipake buat bikin struktur data kayak list, queue, stack yang bisa nampung berbagai tipe. (Class bakal dibahas lebih detail kalau ada materi OOP).
class Koleksi<T> {
private data: T[] = [];
tambah(item: T): void {
this.data.push(item);
}
ambilSemua(): T[] {
return this.data;
}
}
let koleksiNama = new Koleksi<string>();
koleksiNama.tambah("Budi");
koleksiNama.tambah("Ani");
// koleksiNama.tambah(123); // ERROR!
let koleksiAngka = new Koleksi<number>();
koleksiAngka.tambah(10);
Generic Constraints (Batasan Tipe Generic) - Biar Gak Terlalu Bebas
Kadang, kita mau type variable T
kita itu gak bener-bener bisa jadi tipe apa aja, tapi harus punya "sifat" atau properti tertentu. Misalnya, kita mau bikin fungsi yang ngitung panjang sesuatu, jadi argumennya harus punya properti length
. Di sinilah generic constraints dipake, biasanya pake keyword extends
.
interface PunyaPanjang {
length: number; // Semua yang punya properti length bertipe number
}
// Fungsi ini nerima T, TAPI T-nya harus punya properti 'length'
function cetakPanjang<T extends PunyaPanjang>(arg: T): void {
console.log("Panjangnya adalah:", arg.length);
}
cetakPanjang("Halo Dunia"); // Oke, string punya .length
cetakPanjang([1, 2, 3, 4]); // Oke, array punya .length
// cetakPanjang(123); // ERROR! Angka gak punya .length
// cetakPanjang({nama: "Budi"}); // ERROR! Objek ini gak punya .length (kecuali didefinisiin)
let objekDenganPanjang = { length: 10, isi: "sesuatu" };
cetakPanjang(objekDenganPanjang); // Oke
Dengan T extends PunyaPanjang
, kita ngasih batasan kalau T
itu minimal harus "memenuhi kontrak" dari interface PunyaPanjang
.
Generics ini adalah konsep yang lumayan advance di TypeScript, tapi super powerful buat nulis kode yang DRY (Don't Repeat Yourself), fleksibel, dan tetep type-safe.
Buat awal, cukup pahamin dulu gimana cara pake generics di fungsi buat nerima dan ngembaliin tipe yang sama atau berhubungan. Seiring kamu makin jago TypeScript dan kerja sama tipe data yang lebih kompleks, kamu bakal makin ngerasain manfaat dari generics ini.
Ini kayak punya satu "cetakan kue" ajaib yang bisa kamu pake buat bikin macem-macem jenis kue (tipe data) dengan bentuk dasar yang sama!
Kuis Generics Dasar TypeScript
Pertanyaan 1 dari 5
Apa tujuan utama dari penggunaan Generics di TypeScript?