Jurusan Maut Custom Type Guard Biar TypeScript Kamu Lebih Pintar dari Compiler Biasa

Jurusan Maut Custom Type Guard Biar TypeScript Kamu Lebih Pintar dari Compiler Biasa
Photo by Patrick Martin/Unsplash

Ngoding pakai TypeScript itu ibarat punya safety belt digital. Kita merasa aman karena ada si compiler yang cerewet, selalu ngecek tipe data kita. Tapi, pernah enggak sih kamu merasa si compiler ini kadang terlalu kaku, bahkan terkesan kurang pintar?

Dia tahu number itu angka dan string itu teks. Dia tahu kalau kamu pakai instanceof Date, berarti objek itu pasti tanggal. Tapi begitu kamu punya custom type yang kompleks—misalnya, union type antara AdminUser dan GuestUser—compiler mulai bingung. Dia cuma tahu objek itu adalah salah satu dari mereka, tapi dia enggak tahu yang mana sampai kamu kasih tahu secara eksplisit.

Di sinilah Custom Type Guard jadi jagoan. Ini adalah salah satu superskill yang wajib dikuasai kalau kamu mau code TypeScript kamu naik kelas, lebih bersih, lebih aman, dan yang pasti, lebih elegan. Di Javapixa Creative Studio, kami sering pakai teknik ini, terutama saat berhadapan dengan data API yang tipenya beragam. Kita enggak mau cuma ngandelin casting yang risikonya gede. Kita mau si compiler bener-bener tahu apa yang sedang dia hadapi.

Custom Type Guard memungkinkan kita 'mengajari' compiler TypeScript kita untuk mengenali struktur data yang spesifik, bahkan dalam skenario union type yang paling rumit sekalipun. Ini bukan sekadar hack, ini adalah fitur inti yang bakal membuat code kamu jadi smart.

Yuk, kita bedah jurus maut ini sampai tuntas.

---

Kenapa Type Guard Biasa Nggak Cukup?

Sebelum kita masuk ke jurus utamanya, mari kita lihat dulu Type Guard bawaan yang sering kita pakai sehari-hari.

  1. typeof: Digunakan untuk tipe data primitif (string, number, boolean, undefined, object, function, symbol, bigint).
typescript
    function printLength(x: string | number) {
      if (typeof x === 'string') {
        // Di sini, TypeScript tahu x pasti string.
        console.log(x.length);
      }
    }
  1. instanceof: Digunakan untuk mengecek apakah suatu objek berasal dari kelas tertentu.
typescript
    class Dog { bark() {} }
    class Cat { meow() {} }

Kedua cara ini hebat, tapi bagaimana kalau kita punya interface khusus untuk data dari backend?

Misalnya, kita punya data pengguna.

typescript
interface SuccessResponse {
  status: 'success';
  data: any;
}interface ErrorResponse {
  status: 'error';
  message: string;
}

Kalau kamu menerima objek bertipe APIResponse, gimana cara kamu tahu objek itu sukses atau error? Kamu enggak bisa pakai typeof karena keduanya adalah object. Kamu juga enggak bisa pakai instanceof karena keduanya cuma interface (yang hilang saat di-compile ke JavaScript).

Kamu harus mengecek properti di dalamnya, misalnya response.status === 'success'. Ini adalah logika JavaScript. Tapi bagaimana cara kamu meyakinkan TypeScript bahwa jika status adalah 'success', maka objek itu pasti memiliki properti data (yang ada di SuccessResponse)?

Di sinilah Custom Type Guard masuk, menggunakan kata kunci ajaib: is.

---

Anatomis Custom Type Guard: Jurus is

Custom Type Guard hanyalah sebuah fungsi yang mengembalikan nilai boolean, tapi dengan return type signature yang spesial: parameter is TypeName.

Syntax-nya kira-kira begini:

typescript
function namaFungsi(input: unknown): input is TipeTarget {
  // Logika pemeriksaan data di sini
}

Mari kita terapkan pada contoh APIResponse tadi.

typescript
interface SuccessResponse {
  status: 'success';
  data: { userId: number; token: string };
}interface ErrorResponse {
  status: 'error';
  message: string;
}type APIResponse = SuccessResponse | ErrorResponse;// ---------- Custom Type Guard Kita ----------
function isSuccessResponse(response: APIResponse): response is SuccessResponse {
  // Kita cek apakah properti 'status' sama dengan 'success'.
  // Kita juga bisa menambahkan pengecekan keamanan lain, misalnya memastikan
  // properti 'data' itu ada, meskipun dalam union type ini sudah cukup dengan 'status'.
  return (response as SuccessResponse).status === 'success';
}

Lihat betapa rapihnya kode di dalam handleResponse? Tidak ada lagi res as SuccessResponse yang berbahaya, tidak ada lagi tanda seru ! yang mematikan fitur keamanan TypeScript. Kita sudah memberi tahu compiler, "Hei, kalau fungsi isSuccessResponse ini balik true, maka anggap saja res itu pasti SuccessResponse."

Ini adalah standar kebersihan kode yang kami terapkan di Javapixa Creative Studio untuk memastikan data handling selalu solid dan maintainable.

---

Type Guard untuk Union Type yang Lebih Kompleks

Seringkali, data yang kita terima dari API jauh lebih rumit daripada sekadar sukses dan error. Kita mungkin berhadapan dengan payload yang memiliki struktur berbeda-beda tapi dikirim ke endpoint yang sama.

Bayangkan kita sedang membuat fitur feed yang bisa menampilkan Postingan, Iklan, atau Pemberitahuan.

typescript
interface Post {
  type: 'post';
  content: string;
  authorId: number;
}interface Ad {
  type: 'ad';
  campaignId: string;
  imageUrl: string;
}interface Notification {
  type: 'notification';
  message: string;
  read: boolean;
}

Bagaimana cara kita memproses array berisi FeedItem ini? Kita butuh Type Guard untuk masing-masing tipe.

typescript
// 1. Guard untuk Post
function isPost(item: FeedItem): item is Post {
  // Kita cek diskriminator property 'type'
  return item.type === 'post' && 'authorId' in item;
}// 2. Guard untuk Ad
function isAd(item: FeedItem): item is Ad {
  return item.type === 'ad' && 'campaignId' in item;
}// 3. Guard untuk Notification
function isNotification(item: FeedItem): item is Notification {
  // Karena kita bekerja dengan union type, pengecekan 'type' saja seringnya sudah cukup,
  // tapi menambahkan pengecekan properti unik ('read' in item) adalah praktik yang sangat baik
  // untuk berjaga-jaga jika ada data kotor (dirty data) dari luar.
  return item.type === 'notification' && 'read' in item;
}

Dengan teknik ini, kita berhasil memecah kerumitan union type yang besar menjadi bagian-bagian kecil yang terkelola dengan baik, memastikan kita tidak pernah salah mengakses properti yang tidak ada.

---

Aplikasi Praktis: Membersihkan Array dengan Type Guard

Salah satu skenario paling kuat dari Custom Type Guard adalah ketika kamu harus memfilter sebuah array yang berisi tipe data campuran (any atau union type), dan kamu hanya ingin mengambil item yang memiliki tipe spesifik.

Bayangkan kita mendapatkan array FeedItem[], tapi kita hanya mau memproses Post. Array tersebut mungkin mengandung data yang hilang atau rusak.

Tanpa Type Guard, kamu mungkin akan melakukan casting atau menggunakan filter yang hasilnya masih FeedItem[] (karena TypeScript tidak tahu filter kamu berhasil).

Dengan Custom Type Guard, kita bisa membuat array hasil filter memiliki tipe yang spesifik.

typescript
// Kita gunakan fungsi isPost yang sudah kita buat sebelumnya.const allItems: FeedItem[] = [
  { type: 'post', content: 'Halo dunia', authorId: 101 },
  { type: 'ad', campaignId: 'XMAS', imageUrl: '...' },
  { type: 'post', content: 'TypeScript Power', authorId: 102 },
];// MAGIC: Custom Type Guard berfungsi sempurna di dalam Array.prototype.filter()
const onlyPosts = allItems.filter(isPost);// Tipe data 'onlyPosts' sekarang adalah: Post[]
console.log(onlyPosts.map(p => p.content));

Ini adalah game changer kalau kamu sering melakukan validasi atau pembersihan data di sisi frontend atau backend berbasis Node.js. Di Javapixa Creative Studio, kita memastikan proses data mapping selalu seketat ini.

---

Tips Profesional ala Javapixa Creative Studio

Menggunakan Custom Type Guard itu mudah, tapi ada beberapa best practice agar kode kamu tetap konsisten dan maintainable.

1. Jaga Kemurnian (Purity) Fungsi Guard

Fungsi Custom Type Guard seharusnya hanya melakukan pengecekan tipe dan mengembalikan boolean (dengan klausa is). Fungsi ini tidak boleh memiliki side effects (misalnya, mengubah state global, memanggil API, atau memodifikasi argumen yang dilewatkan).

Sebuah Type Guard yang baik adalah murni: menerima input, mengecek tipe, dan mengembalikan hasil, tanpa melakukan hal lain.

2. Selalu Mulai dengan Pengecekan Primitif

Sebelum kamu mulai mengecek properti yang dalam (misalnya item.prop.value), pastikan dulu item itu sendiri adalah objek dan bukan null atau undefined. Gunakan typeof x === 'object' && x !== null.

Ini penting untuk mencegah runtime error di JavaScript.

typescript
function isUserObject(obj: unknown): obj is User {
  if (typeof obj !== 'object' || obj === null) {
    return false;
  }
  // Baru cek properti:
  return 'name' in obj && typeof obj.name === 'string';
}

3. Hati-Hati dengan Properti Opsional

Jika kamu mengandalkan properti opsional (misalnya prop?: string) untuk membedakan tipe, maka Custom Type Guard harus lebih teliti. Cek apakah properti tersebut ada (menggunakan in operator) dan bukan hanya mengecek nilainya.

typescript
// Contoh: User A punya alamat, User B tidak
interface UserWithAddress { address: string; }
interface UserNoAddress { }

4. Konsistensi Penamaan

Biasanya, fungsi Type Guard diberi awalan is (e.g., isPost, isAdmin, isError). Ini membuat kodenya mudah dibaca dan dipahami oleh developer lain. Ini adalah standar yang kami rekomendasikan di setiap proyek Javapixa Creative Studio.

---

Type Guard Maut untuk Error Handling

Satu lagi area di mana Type Guard sangat membantu adalah dalam error handling, terutama dalam blok try...catch.

Secara default, tipe data variabel e dalam blok catch (e) adalah unknown (sejak TypeScript 4.4). Ini adalah peningkatan keamanan karena exception di JavaScript bisa berupa apa saja (string, number, objek, dll.).

Kita sering ingin memastikan bahwa error yang kita tangani adalah objek Error standar atau bahkan CustomAPIError kita sendiri.

typescript
// Custom Guard untuk memastikan objek e adalah Error standar
function isError(e: unknown): e is Error {
  // Cek apakah itu objek dan apakah ia memiliki properti 'message' dan 'name'
  return typeof e === 'object' && e !== null && 'message' in e && 'name' in e;
}

Dengan isError, kamu memastikan bahwa kamu hanya mencoba mengakses properti .message ketika kamu yakin properti itu ada, menghindari crash tak terduga.

---

Penutup: Upgrade Compiler Kamu Sekarang

Custom Type Guard adalah fitur TypeScript yang enggak boleh kamu anggap sepele. Ini adalah 'jembatan' antara logika kode JavaScript kamu dengan sistem tipe yang ketat dari TypeScript.

Dengan menguasai teknik is Type, kamu tidak hanya membuat kode kamu lebih aman dari runtime error, tapi juga membuat developer experience (DX) di tim kamu jauh lebih menyenangkan. Kamu menghapus keraguan, memaksa compiler untuk 'mengerti' desain arsitektur data custom kamu.

Di dunia pengembangan aplikasi yang cepat dan kompleks seperti yang kami hadapi di Javapixa Creative Studio, penggunaan Custom Type Guard adalah keharusan. Ini adalah kunci untuk membangun aplikasi yang robust, scalable, dan mudah di-maintain.

Jadi, tunggu apa lagi? Jadikan Custom Type Guard sebagai senjata andalanmu. Bikin compiler TypeScript kamu bekerja lebih keras dan lebih pintar!