Bagaimana Menggunakan Generics pada TypeScript

Ajat DarojatAjat Darojat
5 min read

Selamat datang pada pembahasan Typescript! Hari ini, kita akan mempelajari generik, sebuah feature di Typescript yang powerful yang memungkinkan kita untuk membuat kemponen yang dapat digunakan kembali. Generik memberikan sebuah cara untuk membuat komponen-komponen yang berkerja dengan banyak tipe data dan memberikan kemanan tipe pada saat yang bersamaan.

1. Perkenalan Generik

Generik memungkinkan kita untuk menulis fungsi, kelas dan interface yang fleksibel dan dapat digunakan berulang-ulang. Generik memungkinkan kita membuat komponen-komponen yang dapat bekerja dengan banyak tipe data tanpa kehilangan informasi tipenya.

Fungsi Generik Dasar

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

let output1 = identity<string>("myString"); // Output: "myString"
let output2 = identity<number>(42); // Output: 42

Pada contoh ini, T adalah tipe parameter yang bertindak sebagai sebuah placeholder untuk tipe yang asli. Ketika fungsinya dipanggil, tipe parameternya telah diganti dengan tipe yang diberikan.

2. Fungsi Generik

Kita dapat membuat fungsi generik yang bekerja dengan banyak tipe data. Ini berguna khususnya untuk fungsi yang bekerja dengan array atau koleksi.

Contoh: Fungsi Generik

function loggingIdentity<T>(arg: T[]): T[] {
  console.log(arg.length); // Array has a `.length` property
  return arg;
}

let stringArray = loggingIdentity<string>(["Alice", "Bob", "Charlie"]);

Disini, fungsi loggingIdentity bekerja dengan sebuah array dari semua tipe T. Tipe array ditentukan ketika fungsinya dipanggil.

3. Kelas Generik

Generik dapat digunakan untuk membuat kelas yang bekerja dengan berbagai tipe. Ini berguna untuk struktur data seperti stacks, queues atau linked lists.

Contoh: Kelas Generik

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

  constructor(zeroValue: T, addFunction: (x: T, y: T) => T) {
    this.zeroValue = zeroValue;
    this.add = addFunction;
  }
}

let myNumber = new GenericNumber<number>(0, (x, y) => x + y);
console.log(myNumber.add(5, 10)); // Output: 15

let myString = new GenericNumber<string>("", (x, y) => x + y);
console.log(myString.add("Hello, ", "world!")); // Output: "Hello, world!"

Pada contoh ini, kelas GenericNumber bekerja dengan berbagai tipe T. Tipenya dispesifikasikan ketika sebuah instance dari kelas dibuat.

4. Constraints Generik

Terkadang, kita perlu untuk membatasi tipe-tipe yang dapat digunakan dengan generik. Kita dapat mencapainya dengan constraints.

Contoh: Constraints Generik

interface Lengthwise {
  length: number;
}

function loggingIdentityWithConstraint<T extends Lengthwise>(arg: T): T {
  console.log(arg.length); // Now we know it has a `.length` property
  return arg;
}

loggingIdentityWithConstraint({ length: 10, value: 42 }); // Works
// loggingIdentityWithConstraint(42); // Error: Argument of type 'number' is not assignable to parameter of type 'Lengthwise'.

Disini, tipe generic T telah dibatasi untuk tipe-tipe yang memiliki properti length.

5. Menggunakan Beberapa Parameter Tipe

Generik dapat juga menggunakan beberapa parameter tipe untuk membuat tipe-tipe yang lebih fleksibel dan kompleks.

Contoh: Beberapa Parameter Tipe

function merge<T, U>(obj1: T, obj2: U): T & U {
  return { ...obj1, ...obj2 };
}

let mergedObject = merge({ name: "Alice" }, { age: 25 });
console.log(mergedObject); // Output: { name: "Alice", age: 25 }

Pada contoh ini, fungsi merge menerima 2 tipe objek yang berbeda dan menggabungkannya menjadi satu.

6. Interfaces Generik

Interfaces dapat juga menggunakan generics untuk membuat kontrak untuk fungsi, kelas dan objek yang fleksibel dan dapat digunakan kembali.

Contoh: Interfaces Generik

interface KeyValuePair<K, V> {
  key: K;
  value: V;
}

let kvp: KeyValuePair<string, number> = { key: "age", value: 30 };
console.log(kvp); // Output: { key: "age", value: 30 }

Disini, interface keyValuePair menerima dua parameter, K dan V, membuat sebuah generik interface.

7. Parameter Tipe Bawaan

Kita dapat memberikan tipe bawaan untuk generik kit, membuat opsi generik ketika memanggil fungsi atau membuat sebuah instance dari sebuah kelas.

Contoh: Parameter Tipe Bawaan

function createArray<T = string>(length: number, value: T): T[] {
  return Array(length).fill(value);
}

let stringArray = createArray(3, "hello");
let numberArray = createArray<number>(3, 42);
console.log(stringArray); // Output: ["hello", "hello", "hello"]
console.log(numberArray); // Output: [42, 42, 42]

Pada contoh ini, jika tipe tidak diberikan, maka type string bawaan akan digunakan.

8. Constraints dan keyof Generik

Menggunakan typeof dengan generik memungkinkan kita untuk membuat fungsi yang beroperasi pada properti-properti objek yang spesifik.

Contoh: Constraints dan keyof Generik

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

let person = { name: "Alice", age: 25 };
let name = getProperty(person, "name"); // Output: "Alice"
let age = getProperty(person, "age"); // Output: 25
// let unknown = getProperty(person, "unknown"); // Error: Argument of type '"unknown"' is not assignable to parameter of type 'keyof { name: string; age: number; }'.

Disini, fungsi getProperty menerima sebuah object T dan key K yang harus menjadi sebuah key dari T. Ini memastikan keamanan tipe.

Contoh Kode

Disini sebuah contoh yang mencontohkan penggunaan dari generik pada fungsi, kelas dan interface:

// Generic function
function reverseArray<T>(items: T[]): T[] {
  return items.reverse();
}

let reversedNumbers = reverseArray([1, 2, 3, 4, 5]);
console.log(reversedNumbers); // Output: [5, 4, 3, 2, 1]

// Generic class
class DataStorage<T> {
  private data: T[] = [];

  addItem(item: T) {
    this.data.push(item);
  }

  removeItem(item: T) {
    this.data = this.data.filter(i => i !== item);
  }

  getItems(): T[] {
    return [...this.data];
  }
}

let textStorage = new DataStorage<string>();
textStorage.addItem("Alice");
textStorage.addItem("Bob");
textStorage.removeItem("Alice");
console.log(textStorage.getItems()); // Output: ["Bob"]

// Generic interface
interface Pair<T, U> {
  first: T;
  second: U;
}

let pair: Pair<string, number> = { first: "Alice", second: 25 };
console.log(pair); // Output: { first: "Alice", second: 25 }

Rangkuman

Hari ini, kita telah mempelajari generics pada Typescript, membahas fungsi, kelas, constraint, multiple parameter tipe, interface, parameter tipe bawaan, dan operator keyof generik.
Generik adalah sebuah fitur yang powerfull yang memungkinkan kita membuat komponen yang fleksibel, dapat digunakan kembali dan sebagai keamanan tipe.

Sumber

0
Subscribe to my newsletter

Read articles from Ajat Darojat directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ajat Darojat
Ajat Darojat

A software engineer, coding instructor, and creator of Mongoloquent ORM. Active in communities and contribute to open-source projects.