Error Handling & Data dari Luar: Type Guard dan Validasi

Class error kustom, Result type, mengapa JSON.parse dan respons API tidak otomatis aman, serta menulis type guard untuk memvalidasi data tidak dipercaya sebelum dipakai.

Part 22 dari 24 ± 55 menit Gratis

Batas dunia tipe

Ingat bab 1: tipe hanya ada saat kompilasi. Begitu data datang dari luar program — respons API, isi file, input pengguna, localStorage, JSON.parse — TypeScript tidak bisa menjamin bentuknya. Di sinilah banyak bug sungguhan lahir, padahal kode "sudah bertipe".

const data = JSON.parse('{"nama": "Ani"}')   // tipe: any  😬
data.umur.toFixed(2)                          // kompilasi lolos, tapi crash saat jalan

JSON.parse mengembalikan any. Cara aman: terima sebagai unknown, lalu validasi sebelum memakainya.

Type guard untuk memvalidasi

interface Pengguna { nama: string; umur: number }

function adalahPengguna(x: unknown): x is Pengguna {
  return (
    typeof x === "object" &&
    x !== null &&
    "nama" in x && typeof x.nama === "string" &&
    "umur" in x && typeof x.umur === "number"
  )
}

function parsePengguna(json: string): Pengguna | null {
  try {
    const data: unknown = JSON.parse(json)
    return adalahPengguna(data) ? data : null
  } catch {
    return null           // JSON rusak
  }
}

parsePengguna('{"nama":"Ani","umur":20}')   // { nama: "Ani", umur: 20 }
parsePengguna('{"nama":"Ani"}')              // null — umur hilang
parsePengguna('bukan json')                  // null

"nama" in x setelah typeof x === "object" && x !== null mempersempit x sehingga x.nama boleh dibaca. Rantai pemeriksaan ini adalah resep standar validasi manual.

as bukan validasi

const p = JSON.parse(teks) as Pengguna      // ⚠️ menutup mulut compiler, TIDAK memeriksa apa pun
const q = <Pengguna>JSON.parse(teks)        // sama saja

Pakai as hanya bila kamu benar-benar tahu lebih banyak daripada compiler (dan lebih baik memverifikasinya). Untuk data eksternal, gunakan guard di atas atau pustaka validasi.

Pustaka validasi schema

Untuk data kompleks, menulis guard manual melelahkan. Pustaka seperti Zod, Valibot, atau ArkType mendefinisikan skema sekali lalu menurunkan tipe sekaligus memvalidasi saat runtime:

import { z } from "zod"

const SkemaPengguna = z.object({ nama: z.string(), umur: z.number().int().min(0) })
type Pengguna = z.infer<typeof SkemaPengguna>          // tipe diturunkan dari skema

const hasil = SkemaPengguna.safeParse(dataDariApi)
if (hasil.success) console.log(hasil.data.nama)
else console.log(hasil.error.issues)

Class error kustom

class ErrorValidasi extends Error {
  constructor(public readonly kolom: string, pesan: string) {
    super(pesan)
    this.name = "ErrorValidasi"
  }
}
class ErrorTidakDitemukan extends Error {
  constructor(public readonly id: number) {
    super(`Data ${id} tidak ditemukan`)
    this.name = "ErrorTidakDitemukan"
  }
}

try {
  throw new ErrorValidasi("email", "Format email salah")
} catch (e) {
  if (e instanceof ErrorValidasi) console.log(e.kolom, e.message)        // e: ErrorValidasi
  else if (e instanceof ErrorTidakDitemukan) console.log(e.id)
  else throw e                                                           // yang tak dikenal: lempar lagi
}

Result type: error sebagai nilai

Alternatif exception: fungsi mengembalikan hasil yang mencantumkan kemungkinan gagal di tipenya. Compiler memaksa pemanggil menanganinya (ingat discriminated union bab 8):

type Hasil<T, E = string> =
  | { ok: true; nilai: T }
  | { ok: false; galat: E }

function bagi(a: number, b: number): Hasil<number> {
  return b === 0 ? { ok: false, galat: "Dibagi nol" } : { ok: true, nilai: a / b }
}

const h = bagi(8, 2)
if (h.ok) console.log(h.nilai)      // 4
else console.log(h.galat)
PendekatanCocok untuk
throw + class errorKesalahan tak terduga / kondisi luar biasa
Result typeKegagalan yang wajar dan diharapkan (validasi, pencarian)
Mengembalikan nullKasus sederhana "ada/tidak ada"

Operator satisfies

satisfies memeriksa bahwa nilai memenuhi sebuah tipe tanpa mengubah tipe aslinya yang lebih spesifik:

const rute = {
  beranda: "/",
  profil: "/profil",
} satisfies Record<string, string>

rute.beranda    // tetap dikenali sebagai kunci yang ada; typo seperti rute.berandaa → error

Kesalahan umum pemula

  • Memakai as Tipe pada respons API dan menganggapnya aman.
  • Menelan error dengan catch {} kosong sehingga masalah tak terlihat.
  • Melempar string (throw "gagal") alih-alih Error — kehilangan stack trace.

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