Bahasa Indonesia

Jelajahi generik TypeScript tingkat lanjut: batasan, tipe utilitas, inferensi, dan aplikasi praktis untuk menulis kode yang kuat dan dapat digunakan kembali dalam konteks global.

Generik TypeScript: Pola Penggunaan Tingkat Lanjut

Generik TypeScript adalah fitur canggih yang memungkinkan Anda menulis kode yang lebih fleksibel, dapat digunakan kembali, dan aman tipe (type-safe). Fitur ini memungkinkan Anda mendefinisikan tipe yang dapat bekerja dengan berbagai tipe lain sambil mempertahankan pemeriksaan tipe pada waktu kompilasi. Postingan blog ini membahas pola penggunaan tingkat lanjut, memberikan contoh praktis dan wawasan bagi para pengembang dari semua tingkatan, terlepas dari lokasi geografis atau latar belakang mereka.

Memahami Dasar-dasar: Sebuah Rekapitulasi

Sebelum mendalami topik tingkat lanjut, mari kita rekap dasarnya dengan cepat. Generik memungkinkan Anda membuat komponen yang dapat bekerja dengan berbagai tipe, bukan hanya satu tipe tunggal. Anda mendeklarasikan parameter tipe generik di dalam kurung sudut (`<>`) setelah nama fungsi atau kelas. Parameter ini bertindak sebagai placeholder untuk tipe aktual yang akan ditentukan nanti saat fungsi atau kelas digunakan.

Sebagai contoh, fungsi generik sederhana mungkin terlihat seperti ini:

function identity(arg: T): T {
  return arg;
}

Dalam contoh ini, T adalah parameter tipe generik. Fungsi identity menerima argumen bertipe T dan mengembalikan nilai bertipe T. Anda kemudian dapat memanggil fungsi ini dengan berbagai tipe:


let stringResult: string = identity("hello");
let numberResult: number = identity(42);

Generik Tingkat Lanjut: Melampaui Dasar-dasar

Sekarang, mari kita jelajahi cara-cara yang lebih canggih untuk memanfaatkan generik.

1. Batasan Tipe Generik

Batasan tipe memungkinkan Anda membatasi tipe-tipe yang dapat digunakan dengan parameter tipe generik. Ini sangat penting ketika Anda perlu memastikan bahwa sebuah tipe generik memiliki properti atau metode tertentu. Anda dapat menggunakan kata kunci extends untuk menentukan batasan.

Perhatikan contoh di mana Anda ingin sebuah fungsi mengakses properti length:

function loggingIdentity(arg: T): T {
  console.log(arg.length);
  return arg;
}

Dalam contoh ini, T dibatasi untuk tipe-tipe yang memiliki properti length bertipe number. Ini memungkinkan kita untuk mengakses arg.length dengan aman. Mencoba memberikan tipe yang tidak memenuhi batasan ini akan menghasilkan kesalahan waktu kompilasi.

Aplikasi Global: Ini sangat berguna dalam skenario yang melibatkan pemrosesan data, seperti bekerja dengan array atau string, di mana Anda sering kali perlu mengetahui panjangnya. Pola ini bekerja dengan cara yang sama, tidak peduli apakah Anda berada di Tokyo, London, atau Rio de Janeiro.

2. Menggunakan Generik dengan Interface

Generik bekerja secara mulus dengan interface, memungkinkan Anda untuk mendefinisikan definisi interface yang fleksibel dan dapat digunakan kembali.

interface GenericIdentityFn {
  (arg: T): T;
}

function identity(arg: T): T {
  return arg;
}

let myIdentity: GenericIdentityFn = identity;

Di sini, GenericIdentityFn adalah sebuah interface yang mendeskripsikan fungsi yang menerima tipe generik T dan mengembalikan tipe T yang sama. Ini memungkinkan Anda mendefinisikan fungsi dengan tanda tangan tipe yang berbeda sambil menjaga keamanan tipe.

Perspektif Global: Pola ini memungkinkan Anda membuat interface yang dapat digunakan kembali untuk berbagai jenis objek. Misalnya, Anda dapat membuat interface generik untuk objek transfer data (DTO) yang digunakan di berbagai API, memastikan struktur data yang konsisten di seluruh aplikasi Anda terlepas dari wilayah tempat aplikasi tersebut diterapkan.

3. Kelas Generik

Kelas juga bisa bersifat generik:


class GenericNumber {
  zeroValue: T;
  add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

Kelas GenericNumber ini dapat menampung nilai bertipe T dan mendefinisikan metode add yang beroperasi pada tipe T. Anda membuat instance kelas dengan tipe yang diinginkan. Ini bisa sangat membantu untuk membuat struktur data seperti stack atau queue.

Aplikasi Global: Bayangkan sebuah aplikasi keuangan yang perlu menyimpan dan memproses berbagai mata uang (misalnya, USD, EUR, JPY). Anda dapat menggunakan kelas generik untuk membuat kelas `CurrencyAmount` di mana `T` merepresentasikan tipe mata uang, memungkinkan perhitungan dan penyimpanan jumlah mata uang yang berbeda secara aman tipe.

4. Parameter Tipe Ganda

Generik dapat menggunakan beberapa parameter tipe:


function swap(a: T, b: U): [U, T] {
  return [b, a];
}

let result = swap("hello", 42);
// result[0] is number, result[1] is string

Fungsi swap menerima dua argumen dengan tipe yang berbeda dan mengembalikan sebuah tuple dengan tipe yang ditukar.

Relevansi Global: Dalam aplikasi bisnis internasional, Anda mungkin memiliki fungsi yang menerima dua bagian data terkait dengan tipe yang berbeda dan mengembalikannya dalam bentuk tuple, seperti ID pelanggan (string) dan nilai pesanan (number). Pola ini tidak memihak negara tertentu dan beradaptasi secara sempurna dengan kebutuhan global.

5. Menggunakan Parameter Tipe dalam Batasan Generik

Anda dapat menggunakan parameter tipe di dalam sebuah batasan.


function getProperty(obj: T, key: K) {
  return obj[key];
}

let obj = { a: 1, b: 2, c: 3 };

let value = getProperty(obj, "a"); // value is number

Dalam contoh ini, K extends keyof T berarti bahwa K hanya bisa menjadi kunci dari tipe T. Ini memberikan keamanan tipe yang kuat saat mengakses properti objek secara dinamis.

Penerapan Global: Ini sangat berguna saat bekerja dengan objek konfigurasi atau struktur data di mana akses properti perlu divalidasi selama pengembangan. Teknik ini dapat diterapkan dalam aplikasi di negara mana pun.

6. Tipe Utilitas Generik

TypeScript menyediakan beberapa tipe utilitas bawaan yang memanfaatkan generik untuk melakukan transformasi tipe umum. Ini termasuk:

Sebagai contoh:


interface User {
  id: number;
  name: string;
  email: string;
}

// Partial - semua properti opsional
let optionalUser: Partial = {};

// Pick - hanya properti id dan name
let userSummary: Pick = { id: 1, name: 'John' };

Kasus Penggunaan Global: Utilitas ini sangat berharga saat membuat model permintaan dan respons API. Misalnya, dalam aplikasi e-commerce global, Partial dapat digunakan untuk merepresentasikan permintaan pembaruan (di mana hanya beberapa detail produk yang dikirim), sementara Readonly mungkin merepresentasikan produk yang ditampilkan di frontend.

7. Inferensi Tipe dengan Generik

TypeScript sering kali dapat menyimpulkan (infer) parameter tipe berdasarkan argumen yang Anda berikan ke fungsi atau kelas generik. Ini dapat membuat kode Anda lebih bersih dan lebih mudah dibaca.


function createPair(a: T, b: T): [T, T] {
  return [a, b];
}

let pair = createPair("hello", "world"); // TypeScript menyimpulkan T sebagai string

Dalam kasus ini, TypeScript secara otomatis menyimpulkan bahwa T adalah string karena kedua argumen adalah string.

Dampak Global: Inferensi tipe mengurangi kebutuhan akan anotasi tipe eksplisit, yang dapat membuat kode Anda lebih ringkas dan mudah dibaca. Ini meningkatkan kolaborasi di antara tim pengembangan yang beragam, di mana mungkin terdapat tingkat pengalaman yang berbeda.

8. Tipe Kondisional dengan Generik

Tipe kondisional, bersama dengan generik, menyediakan cara yang ampuh untuk membuat tipe yang bergantung pada nilai tipe lain.


type Check = T extends string ? string : number;

let result1: Check = "hello"; // string
let result2: Check = 42; // number

Dalam contoh ini, Check dievaluasi menjadi string jika T merupakan turunan dari string, jika tidak, ia dievaluasi menjadi number.

Konteks Global: Tipe kondisional sangat berguna untuk membentuk tipe secara dinamis berdasarkan kondisi tertentu. Bayangkan sebuah sistem yang memproses data berdasarkan wilayah. Tipe kondisional kemudian dapat digunakan untuk mengubah data berdasarkan format atau tipe data spesifik wilayah tersebut. Ini sangat penting untuk aplikasi dengan persyaratan tata kelola data global.

9. Menggunakan Generik dengan Tipe Terpetakan (Mapped Types)

Tipe terpetakan (mapped types) memungkinkan Anda mengubah properti dari suatu tipe berdasarkan tipe lain. Gabungkan dengan generik untuk fleksibilitas:


type OptionsFlags = {
  [K in keyof T]: boolean;
};

interface FeatureFlags {
  darkMode: boolean;
  notifications: boolean;
}

// Membuat tipe di mana setiap feature flag diaktifkan (true) atau dinonaktifkan (false)
let featureFlags: OptionsFlags = {
  darkMode: true,
  notifications: false,
};

Tipe OptionsFlags menerima tipe generik T dan membuat tipe baru di mana properti dari T sekarang dipetakan ke nilai boolean. Ini sangat kuat untuk bekerja dengan konfigurasi atau feature flag.

Aplikasi Global: Pola ini memungkinkan pembuatan skema konfigurasi berdasarkan pengaturan spesifik wilayah. Pendekatan ini memungkinkan pengembang untuk mendefinisikan konfigurasi spesifik wilayah (misalnya, bahasa yang didukung di suatu wilayah). Ini memungkinkan pembuatan dan pemeliharaan skema konfigurasi aplikasi global dengan mudah.

10. Inferensi Tingkat Lanjut dengan Kata Kunci `infer`

Kata kunci infer memungkinkan Anda mengekstrak tipe dari tipe lain di dalam tipe kondisional.


type ReturnType any> = T extends (...args: any) => infer R ? R : any;

function myFunction(): string {
  return "hello";
}

let result: ReturnType = "hello"; // result adalah string

Contoh ini menyimpulkan tipe kembalian dari sebuah fungsi menggunakan kata kunci infer. Ini adalah teknik canggih untuk manipulasi tipe yang lebih lanjut.

Signifikansi Global: Teknik ini bisa sangat vital dalam proyek perangkat lunak global yang besar dan terdistribusi untuk memberikan keamanan tipe saat bekerja dengan tanda tangan fungsi yang kompleks dan struktur data yang rumit. Ini memungkinkan pembuatan tipe secara dinamis dari tipe lain, meningkatkan kemudahan pemeliharaan kode.

Praktik Terbaik dan Tips

Kesimpulan: Merangkul Kekuatan Generik Secara Global

Generik TypeScript adalah landasan dalam menulis kode yang kuat dan mudah dipelihara. Dengan menguasai pola-pola tingkat lanjut ini, Anda dapat secara signifikan meningkatkan keamanan tipe, kemampuan penggunaan kembali, dan kualitas keseluruhan aplikasi JavaScript Anda. Dari batasan tipe sederhana hingga tipe kondisional yang kompleks, generik menyediakan alat yang Anda butuhkan untuk membangun perangkat lunak yang dapat diskalakan dan dipelihara untuk audiens global. Ingatlah bahwa prinsip-prinsip penggunaan generik tetap konsisten terlepas dari lokasi geografis Anda.

Dengan menerapkan teknik yang dibahas dalam artikel ini, Anda dapat membuat kode yang terstruktur lebih baik, lebih andal, dan mudah diperluas, yang pada akhirnya mengarah pada proyek perangkat lunak yang lebih sukses terlepas dari negara, benua, atau bisnis yang Anda geluti. Rangkullah generik, dan kode Anda akan berterima kasih!