Generics Lanjutan: Constraint, Interface & Class Generik

Membatasi parameter tipe dengan extends, memakai keyof sebagai batasan, nilai default parameter tipe, serta membuat interface dan class generik seperti Tumpukan (Stack).

Part 14 dari 24 ± 55 menit Gratis

Constraint: membatasi T dengan extends

Kadang T terlalu bebas. Jika fungsi butuh properti length, kita harus menyatakan bahwa T minimal punya length:

function terpanjang<T extends { length: number }>(a: T, b: T): T {
  return a.length >= b.length ? a : b
}

terpanjang("kucing", "gajah")        // ✅ string punya length → "kucing"
terpanjang([1, 2], [1, 2, 3])        // ✅ array punya length
terpanjang(10, 20)                   // ❌ Argument of type 'number' is not assignable to parameter of type '{ length: number }'

T extends X dibaca "T harus kompatibel dengan X". Hasilnya tetap T (bukan sekadar { length: number }), jadi tipe asli tidak hilang.

keyof sebagai batasan

Pola paling berguna: mengambil properti object dengan kunci yang dijamin ada:

function ambil<T, K extends keyof T>(obj: T, kunci: K): T[K] {
  return obj[kunci]
}

const pengguna = { id: 1, nama: "Ani", aktif: true }
const n = ambil(pengguna, "nama")      // string
const i = ambil(pengguna, "id")        // number
ambil(pengguna, "email")               // ❌ Argument of type '"email"' is not assignable to parameter of type '"id" | "nama" | "aktif"'

Di sini keyof T menghasilkan gabungan semua nama properti T, dan T[K] adalah tipe nilai pada kunci itu. Keduanya dibahas penuh di bab 16 — yang penting kamu sudah melihat gunanya: autocomplete kunci + tipe hasil yang tepat.

Interface dan type generik

interface Respons<T> {
  sukses: boolean
  data: T
  pesan?: string
}

type DaftarProduk = Respons<{ id: number; nama: string }[]>
const r: Respons<string> = { sukses: true, data: "OK" }

Nilai default untuk parameter tipe

interface Halaman<T = string> {
  isi: T[]
  nomor: number
}
const a: Halaman = { isi: ["a"], nomor: 1 }                 // T = string (default)
const b: Halaman<number> = { isi: [1, 2], nomor: 1 }       // T = number

Class generik: contoh Tumpukan (Stack)

Struktur data adalah rumah alami generics: logikanya sama, isinya berbeda-beda.

class Tumpukan<T> {
  private data: T[] = []

  push(item: T): void { this.data.push(item) }
  pop(): T | undefined { return this.data.pop() }
  lihat(): T | undefined { return this.data[this.data.length - 1] }
  get ukuran(): number { return this.data.length }
}

const angka = new Tumpukan<number>()
angka.push(1)
angka.push(2)
angka.push("tiga")    // ❌ hanya number
angka.pop()           // 2 (tipe: number | undefined)

const teks = new Tumpukan<string>()   // satu class, tipe berbeda

Constraint pada class dan beberapa batasan

interface PunyaId { id: number }

class Repositori<T extends PunyaId> {
  private item = new Map<number, T>()
  simpan(x: T): void { this.item.set(x.id, x) }
  cari(id: number): T | undefined { return this.item.get(id) }
}

const repo = new Repositori<{ id: number; nama: string }>()
repo.simpan({ id: 1, nama: "Ani" })
repo.cari(1)?.nama

Pola Repositori<T> ini sangat umum di aplikasi nyata: satu class untuk menyimpan/menemukan data apa pun yang punya id.

Fungsi generik dengan callback

function kelompokkan<T, K extends string>(daftar: T[], kunci: (x: T) => K): Record<K, T[]> {
  const hasil = {} as Record<K, T[]>
  for (const x of daftar) {
    const k = kunci(x)
    ;(hasil[k] ??= []).push(x)
  }
  return hasil
}

(Jangan khawatir bila baris ini terasa padat — kamu akan terbiasa. Perhatikan bagaimana T dan K saling berhubungan.)

Kesalahan umum pemula

  • Lupa constraint lalu mengakses properti pada T (error: "Property 'length' does not exist on type 'T'").
  • Menulis constraint yang terlalu sempit sehingga fungsi tak bisa dipakai oleh tipe yang wajar.
  • Memakai as untuk memaksa tipe generik padahal bisa diselesaikan dengan constraint yang benar.

Praktikkan langsung, gratis

Tulis dan jalankan kode langsung di browser, dapatkan penilaian otomatis. Daftar gratis kapan saja untuk menyimpan progres dan meraih sertifikat.

Coba latihan sekarang, tanpa daftar
Bagikan: WhatsApp Facebook LinkedIn X