Narrowing Lanjutan & Discriminated Union

Teknik mempersempit tipe: in, instanceof, type predicate (is), dan pola paling penting di TypeScript — discriminated union dengan switch dan pengecekan kelengkapan.

Part 8 dari 24 ± 50 menit Gratis

Mengapa narrowing penting?

Union type (A | B) hanya berguna kalau kita bisa tahu yang mana yang sedang dipegang. Proses menyempitkan union menjadi satu tipe pasti disebut narrowing. Kamu sudah mengenal typeof; bab ini melengkapi perkakasnya.

1. Operator in

Memeriksa apakah sebuah properti ada di object:

type Ikan = { renang: () => void }
type Burung = { terbang: () => void }

function gerak(hewan: Ikan | Burung) {
  if ("renang" in hewan) {
    hewan.renang()       // hewan: Ikan
  } else {
    hewan.terbang()      // hewan: Burung
  }
}

2. instanceof

Untuk instance dari class (termasuk Date, Error, Array):

function tampilkan(x: Date | string): string {
  if (x instanceof Date) return x.toISOString()   // x: Date
  return x.toUpperCase()                          // x: string
}

try { /* ... */ } catch (e) {
  if (e instanceof Error) console.log(e.message)  // e: Error
}

3. Type predicate: penjaga tipe buatan sendiri

Kalau pemeriksaan kamu rumit, bungkus dalam fungsi dengan kembalian khusus parameter is Tipe:

type Kucing = { nama: string; miaw: () => void }
type Anjing = { nama: string; gonggong: () => void }

function adalahKucing(h: Kucing | Anjing): h is Kucing {
  return "miaw" in h
}

function suara(h: Kucing | Anjing) {
  if (adalahKucing(h)) h.miaw()      // h: Kucing
  else h.gonggong()                  // h: Anjing
}

// Berguna untuk filter: buang null dari array
const data: (string | null)[] = ["a", null, "b"]
const bersih = data.filter((x): x is string => x !== null)   // string[]

4. Discriminated union — pola andalan

Beri setiap anggota union satu properti penanda (biasanya bernama jenis, type, atau kind) yang bernilai literal berbeda-beda. TypeScript lalu bisa menentukan anggota mana yang sedang dipakai hanya dari penanda itu.

type Bentuk =
  | { jenis: "lingkaran"; radius: number }
  | { jenis: "persegi"; sisi: number }
  | { jenis: "persegiPanjang"; panjang: number; lebar: number }

function luas(b: Bentuk): number {
  switch (b.jenis) {
    case "lingkaran":
      return Math.PI * b.radius ** 2         // b: {jenis:"lingkaran"; radius}
    case "persegi":
      return b.sisi ** 2
    case "persegiPanjang":
      return b.panjang * b.lebar
  }
}

Di tiap case, TypeScript tahu persis properti apa yang tersedia. Mengakses b.sisi di case "lingkaran" akan error.

Pengecekan kelengkapan (exhaustiveness)

Bagaimana kalau suatu hari kamu menambah bentuk baru tetapi lupa mengurus luas()? Pakai never sebagai jaring pengaman:

function luas(b: Bentuk): number {
  switch (b.jenis) {
    case "lingkaran": return Math.PI * b.radius ** 2
    case "persegi": return b.sisi ** 2
    case "persegiPanjang": return b.panjang * b.lebar
    default: {
      const tidakMungkin: never = b
      throw new Error("Bentuk tak dikenal: " + JSON.stringify(tidakMungkin))
    }
  }
}

Begitu "segitiga" ditambahkan ke Bentuk, baris const tidakMungkin: never = b berubah merah karena b masih bisa berupa segitiga. Compiler memaksa kamu melengkapi semua kasus. Ini pola yang sangat dipakai untuk status, aksi (Redux), dan hasil API.

Contoh nyata: hasil operasi yang berhasil atau gagal

type Hasil<T> =
  | { ok: true; data: T }
  | { ok: false; pesan: string }

function bagi(a: number, b: number): Hasil<number> {
  if (b === 0) return { ok: false, pesan: "Tidak bisa dibagi nol" }
  return { ok: true, data: a / b }
}

const h = bagi(10, 2)
if (h.ok) console.log(h.data)      // data tersedia
else console.log(h.pesan)          // pesan tersedia

(Hasil<T> memakai generics — dibahas di bab 13. Untuk sekarang, baca sebagai "Hasil yang datanya bertipe T".)

TeknikCocok untuk
typeofPrimitif (string, number, boolean)
inObject dengan properti berbeda
instanceofInstance class / Error / Date
Type predicatePemeriksaan rumit yang dipakai berulang
Discriminated unionBeberapa varian data — pilihan utama

Kesalahan umum pemula

  • Memakai properti penanda bertipe string biasa, bukan literal — narrowing tidak bekerja.
  • Lupa return/break pada switch.
  • Menulis type predicate yang berbohong (mengembalikan true padahal bukan tipenya). TypeScript percaya saja pada klaimmu.

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