assertionguardbest-practiceMahir3 mnt baca

Assertion vs Type Guard

Kapan pakai as, kapan pakai guard: matriks keputusan lengkap dengan contoh.

Satu prinsip yang mengatur semuanya

as tidak memeriksa apa-apa. Ia hanya membungkam compiler. Type guard (narrowing, type predicate, assertion function) memeriksa saat runtime lalu memberi tahu compiler hasilnya. Setiap kali kamu menulis as, kamu sedang berkata "saya tanggung jawab atas baris ini". Pertanyaan yang selalu harus ditanya: apakah kamu benar-benar bisa tanggung jawab, atau datanya datang dari luar kendalimu?

Matriks keputusan

SituasiPakaiKenapa
Data dari API / JSON / form / localStorageType guard atau ZodDatanya di luar kendalimu, wajib diverifikasi saat runtime
DOM yang kamu tulis sendirias setelah null checkKamu tahu struktur HTML-nya, compiler tidak
Union yang bisa dipersempitNarrowing biasa (typeof / in / discriminant)Paling aman, nol klaim buta
Butuh cek boolean yang reusableType predicate (x is T)Bisa dipakai di if dan .filter()
Gagal berarti harus throwAssertion function (asserts x is T)Fail fast untuk invariant dan config

Kalau ragu, ikuti urutan ini: narrowing dulu, guard kalau data dari luar, as hanya sebagai pilihan terakhir.

Contoh SALAH vs BENAR, tiga ronde

Ronde 1: data API.

typescript
// SALAH: percaya mentah-mentah pada response
const user = (await res.json()) as User;
tampilkanProfil(user); // kalau API error, crash di dalam

// BENAR: verifikasi dulu, baru pakai
const raw: unknown = await res.json();
if (adalahUser(raw)) {
  tampilkanProfil(raw); // raw: User, terbukti saat runtime
} else {
  tampilkanError("data user tidak valid");
}

Ronde 2: localStorage.

typescript
// SALAH: isi localStorage bisa diubah atau dihapus user kapan saja
const settings = JSON.parse(localStorage.getItem("settings")!) as Settings;
terapkanTema(settings.tema); // settings bisa null -> crash

// BENAR: parse sebagai unknown, validasi, sediakan default
const rawSettings: unknown = JSON.parse(localStorage.getItem("settings") ?? "null");
const settings = adalahSettings(rawSettings) ? rawSettings : defaultSettings;
terapkanTema(settings.tema); // selalu aman

Ronde 3: DOM milik sendiri.

typescript
// BOLEH: kamu yang menulis <canvas id="papan"> di HTML
const kanvas = document.getElementById("papan") as HTMLCanvasElement;
const ctx = kanvas.getContext("2d");

// Tapi kalau elemennya opsional, null check dulu baru as:
const el = document.getElementById("opsional");
if (el) {
  const kanvas2 = el as HTMLCanvasElement; // aman, sudah pasti ada
}

Error compiler yang relevan di perbatasan ini

Dua error yang sering muncul saat orang bimbang antara assertion dan guard:

typescript
// 1. Assertion antar tipe yang tidak berhubungan langsung ditolak:
const n = "123" as number;
text
error TS2352: Conversion of type 'string' to type 'number' may be a mistake
  because neither type sufficiently overlaps with the other.
typescript
// 2. Non-null assertion (!) pada elemen yang ternyata tidak ada:
const el2 = document.getElementById("tidak-ada")!;
el2.textContent = "halo";
text
runtime: TypeError: Cannot set properties of null (setting 'textContent')

! adalah sepupu as: klaim tanpa verifikasi ("saya yakin ini tidak null"). Compiler percaya, runtime yang menentukan. Error kedua ini bahkan tidak ditangkap compiler sama sekali.

Checklist sebelum menulis as

Tanya tiga hal ini berurutan, berhenti di yang pertama yang jawabannya ya:

  1. Bisakah pakai narrowing biasa? Kalau ya, pakai itu. Nol risiko, nol klaim.
  2. Datanya dari luar? (API, input user, storage, URL) Kalau ya, pakai guard atau Zod. as dilarang di sini.
  3. Informasinya cuma ada di kepalamu? (struktur DOM yang kamu tulis, kontrak internal antar modul yang tidak bisa dilihat compiler) Barulah as boleh dipakai, idealnya tepat setelah null check.

Catatan teknis: Aktifkan lint rule @typescript-eslint/no-unnecessary-type-assertion di project. Rule ini menemukan as yang sebenarnya tidak dibutuhkan, dan jumlahnya biasanya mengejutkan. Banyak assertion ditulis karena malas narrowing, padahal narrowing selalu lebih aman.

Tantangan

Audit assertion

Diberikan 3 potong kode ber-assertion. Tentukan mana yang AMAN (tetap pakai as) dan mana yang BERBAHAYA (ganti jadi guard). Tulis ulang yang berbahaya dengan type predicate sederhana.

typescript
// 1. const canvas = document.getElementById("c") as HTMLCanvasElement;
// 2. const user = JSON.parse(localStorage.getItem("u")!) as User;
// 3. function proses(x: string | string[]) {
//      const arr = x as string[];
//      console.log(arr.length);
//    }