Masukkan Password
53 Type Guards di TypeScript
Lecture ini membahas Type Guards di TypeScript. Konsepnya sebenarnya cukup sederhana kalau kita pahami masalah yang ingin diselesaikan.
1. Apa itu Type Guard?
Type Guard adalah cara untuk mengecek tipe suatu value saat program sedang berjalan, lalu TypeScript menggunakan hasil pengecekan tersebut untuk mempersempit (narrow) tipe data.
Kata kuncinya:
Type Guard = mengecek tipe → TypeScript jadi tahu tipe yang lebih spesifik.
Misalnya kita punya:
let value: string | number;
Artinya value bisa berupa:
"hello"
atau:
123
Masalahnya, TypeScript belum tahu saat kita menulis kode:
value.toUpperCase();
Apakah value adalah string?
Bisa jadi number.
Maka kita perlu melakukan pengecekan:
if (typeof value === "string") {
console.log(value.toUpperCase());
}
Di dalam if, TypeScript sekarang tahu:
value
adalah string.
Inilah yang disebut type narrowing menggunakan type guard.
2. Kenapa Type Guard diperlukan?
Bayangkan kita punya:
type StringOrNumber = string | number;
Kemudian:
function addition(a: StringOrNumber, b: StringOrNumber) {
return a + b;
}
Kita mungkin berpikir:
“Kan
a + bbisa untuk string dan number?”
Memang benar.
Tapi masalahnya TypeScript melihat:
a: string | number
b: string | number
Artinya masing-masing bisa berupa string atau number.
Ada beberapa kemungkinan:
string + string
string + number
number + string
number + number
TypeScript tidak mau asal berasumsi.
Karena itu kita perlu memberi tahu TypeScript:
“Kalau salah satu adalah string, perlakukan sebagai string. Kalau keduanya number, perlakukan sebagai number.”
3. Type Guard dengan typeof
Salah satu type guard paling umum adalah:
typeof
Contohnya:
typeof value === "string"
JavaScript akan mengecek tipe value tersebut saat runtime.
Contoh:
console.log(typeof "hello");
hasil:
string
Sedangkan:
console.log(typeof 123);
hasil:
number
Beberapa hasil typeof yang umum:
typeof "hello" → "string"
typeof 123 → "number"
typeof true → "boolean"
typeof undefined → "undefined"
typeof {} → "object"
typeof [] → "object"
typeof function(){} → "function"
4. Contoh Type Guard typeof
Misalnya:
type StringOrNumber = string | number;
Kemudian:
function addition(
a: StringOrNumber,
b: StringOrNumber
) {
if (typeof a === "string" || typeof b === "string") {
return a.toString() + b.toString();
} else {
return a + b;
}
}
Sekarang kita melakukan pengecekan tipe.
Bagian ini:
typeof a === "string" || typeof b === "string"
berarti:
“Apakah
astring ATAUbstring?”
Kalau iya, kita masuk ke blok:
{
return a.toString() + b.toString();
}
5. Mari kita lihat hasilnya
Kasus 1
addition("Hello", "World");
Karena:
typeof a === "string"
adalah true.
Maka:
"Hello" + "World"
hasilnya:
HelloWorld
Kasus 2
addition(20, 30);
Keduanya number.
Jadi kondisi:
typeof a === "string" || typeof b === "string"
adalah:
false
Masuk ke else:
return a + b;
hasil:
50
Kasus 3
addition("Age: ", 20);
a adalah string.
Jadi masuk ke if.
Kemudian:
a.toString()
dan:
b.toString()
menghasilkan:
"Age: " + "20"
hasil:
Age: 20
6. Kenapa TypeScript bisa tahu tipe setelah if?
Ini bagian yang sangat penting.
Misalnya:
function test(value: string | number) {
if (typeof value === "string") {
value.toUpperCase();
}
}
Sebelum if, TypeScript berpikir:
value = string | number
Tetapi setelah:
typeof value === "string"
TypeScript menyimpulkan:
value = string
sehingga method:
value.toUpperCase()
aman digunakan.
Proses ini disebut:
Type Narrowing
Jadi hubungan keduanya:
Type Guard
↓
pengecekan tipe
↓
Type Narrowing
↓
TypeScript mengetahui tipe yang lebih spesifik
7. Type Guard kedua: instanceof
Sekarang kita masuk ke kasus yang berbeda.
Kalau typeof cocok untuk tipe sederhana seperti:
string
number
boolean
bagaimana dengan class?
Misalnya:
class Animal {
makeSound() {
console.log("Generic animal sound");
}
}
Kemudian kita punya:
class Dog extends Animal {
bark() {
console.log("Woof!");
}
}
Ingat konsep inheritance:
Animal
↑
|
Dog
Dog mewarisi Animal.
Jadi object Dog mempunyai:
makeSound()
dan:
bark()
8. Masalah dengan parameter Animal
Sekarang kita buat:
function makeCreatureSound(creature: Animal) {
creature.makeSound();
}
Ini aman.
Karena Animal mempunyai:
makeSound()
Tetapi bagaimana kalau:
creature.bark();
TypeScript akan menolak.
Kenapa?
Karena:
creature: Animal
TypeScript hanya menjamin bahwa creature memiliki apa yang dimiliki Animal.
Sedangkan bark() hanya ada di:
Dog
Bukan di:
Animal
9. Kenapa tidak bisa menggunakan typeof?
Kita mungkin mencoba:
if (typeof creature === "Dog") {
creature.bark();
}
Ini tidak benar.
Kenapa?
Karena typeof di JavaScript tidak memberi nama class.
Misalnya:
const dog = new Dog();
console.log(typeof dog);
hasilnya:
object
Bukan:
Dog
Begitu juga:
const animal = new Animal();
console.log(typeof animal);
hasilnya:
object
Jadi:
new Animal() → object
new Dog() → object
typeof tidak bisa membedakan keduanya berdasarkan nama class.
10. Gunakan instanceof
Untuk object yang berasal dari class, kita bisa menggunakan:
instanceof
Contoh:
if (creature instanceof Dog) {
creature.bark();
}
Artinya:
“Apakah
creaturemerupakan instance dari classDog?”
Kalau iya:
true
Kalau bukan:
false
11. Contoh lengkap instanceof
class Animal {
makeSound() {
console.log("Generic animal sound");
}
}
class Dog extends Animal {
bark() {
console.log("Woof!");
}
}
function makeCreatureSound(creature: Animal) {
if (creature instanceof Dog) {
creature.bark();
} else {
creature.makeSound();
}
}
Kemudian:
const animal = new Animal();
const dog = new Dog();
makeCreatureSound(animal);
makeCreatureSound(dog);
Hasil:
Generic animal sound
Woof!
12. Kenapa instanceof bisa melakukan narrowing?
Sebelum pengecekan:
creature: Animal
TypeScript tahu creature adalah Animal.
Ketika kita melakukan:
if (creature instanceof Dog)
TypeScript menjadi lebih spesifik:
creature: Dog
Karena itu kita sekarang boleh:
creature.bark();
Padahal sebelumnya tidak boleh.
Sekali lagi:
sebelum pengecekan
Animal
↓
instanceof Dog
↓
setelah pengecekan
Dog
13. Type Guard ketiga: in
Sekarang masuk ke kasus yang berbeda lagi.
Misalnya kita memiliki interface:
interface User {
name: string;
email?: string;
}
Perhatikan:
email?: string;
Tanda ? berarti optional.
Jadi object ini valid:
const user1: User = {
name: "John"
};
dan ini juga valid:
const user2: User = {
name: "Mark",
email: "mark@gmail.com"
};
14. Masalah property optional
Misalnya:
function greetUser(user: User) {
console.log(
`Hello ${user.name}, your email is ${user.email}`
);
}
Kalau kita jalankan:
greetUser({
name: "John"
});
hasilnya:
Hello John, your email is undefined
Kenapa?
Karena email tidak diberikan.
Jadi:
user.email
bernilai:
undefined
15. Kita bisa mengecek apakah property ada
Di sinilah operator:
in
bisa digunakan.
Contoh:
if ("email" in user) {
console.log(user.email);
}
Artinya:
“Apakah property
user?”
Kalau ada:
true
Kalau tidak ada:
false
16. Contoh lengkap dengan in
interface User {
name: string;
email?: string;
}
function greetUser(user: User) {
if ("email" in user) {
console.log(
`Hello ${user.name}, your email is ${user.email}`
);
} else {
console.log(`Hello ${user.name}`);
}
}
Kemudian:
greetUser({
name: "John"
});
hasil:
Hello John
Sedangkan:
greetUser({
name: "Mark",
email: "mark@gmail.com"
});
hasil:
Hello Mark, your email is mark@gmail.com
17. Kenapa menggunakan "email" in user?
Perhatikan syntax-nya:
"email" in user
Bukan:
"email" in User
dan bukan:
typeof user === User
Karena User di sini hanyalah:
interface User
Interface tidak menjadi object nyata saat JavaScript dijalankan.
TypeScript interface hanya membantu saat development/compile time.
Jadi JavaScript tidak memiliki:
User
sebagai class yang bisa dibuat instance-nya.
18. Kenapa tidak bisa instanceof dengan interface?
Misalnya:
interface User {
name: string;
email?: string;
}
Kita tidak bisa:
user instanceof User
Karena User bukan class.
Kita hanya bisa melakukan instanceof terhadap sesuatu yang memang ada sebagai constructor/class saat runtime.
Contoh:
class User {
name: string;
constructor(name: string) {
this.name = name;
}
}
Barulah:
user instanceof User
bisa digunakan.
19. Jadi tiga Type Guard utama dalam lecture ini
Lecture ini membahas tiga teknik utama:
| Type Guard | Digunakan untuk | Contoh |
|---|---|---|
typeof | tipe sederhana | string, number, boolean |
instanceof | object/class | Dog, Animal |
in | mengecek property | "email" in user |
20. Cara gampang mengingatnya
Bayangkan kita sedang bertanya kepada JavaScript.
typeof
Kita bertanya:
“Ini tipe datanya apa?”
typeof value === "string"
instanceof
Kita bertanya:
“Object ini berasal dari class apa?”
creature instanceof Dog
in
Kita bertanya:
“Object ini punya property ini atau tidak?”
"email" in user
21. Perbedaan typeof, instanceof, dan in
Misalnya:
const name = "John";
Gunakan:
typeof name === "string"
Karena kita ingin mengecek tipe primitive.
Misalnya:
const dog = new Dog();
Gunakan:
dog instanceof Dog
Karena kita ingin mengecek class/object.
Misalnya:
const user = {
name: "John",
email: "john@gmail.com"
};
Gunakan:
"email" in user
Karena kita ingin mengecek apakah property tertentu ada.
22. Hal paling penting: Type Guard ≠ sekadar pengecekan
Jangan menganggap Type Guard hanya seperti:
if (...)
Yang membuatnya penting adalah TypeScript menggunakan hasil pengecekan tersebut untuk mempersempit tipe.
Contoh:
function print(value: string | number) {
if (typeof value === "string") {
// value sekarang dianggap string
value.toUpperCase();
} else {
// value sekarang dianggap number
value.toFixed(2);
}
}
Sebelum if:
string | number
Setelah typeof:
if → string
else → number
Ini yang disebut type narrowing.
23. Hubungannya dengan Union Type
Type Guard sangat sering digunakan bersama Union Type.
Misalnya:
type ID = string | number;
Kita tidak bisa sembarangan melakukan operasi yang hanya berlaku untuk satu tipe.
Maka:
function printId(id: ID) {
if (typeof id === "string") {
console.log(id.toUpperCase());
} else {
console.log(id.toFixed());
}
}
Secara konsep:
ID
string | number
|
typeof check
/ \
/ \
string number
↓ ↓
toUpperCase() toFixed()
Ini adalah salah satu pola yang sangat sering muncul di TypeScript.
24. Analogi sederhana
Bayangkan kamu punya sebuah kotak bertuliskan:
“Bisa berisi apel ATAU jeruk.”
Kita belum tahu isinya.
Apple | Orange
Kemudian kamu membuka kotaknya dan mengecek:
“Kalau ini apel…”
Maka setelah pengecekan kamu boleh melakukan sesuatu yang khusus untuk apel.
Begitu juga TypeScript.
Sebelum pengecekan:
Apple | Orange
Setelah pengecekan:
Apple
Type Guard adalah cara kita mengatakan kepada TypeScript:
“Saya sudah mengecek kondisi ini. Di bagian kode ini, value-nya pasti tipe tertentu.”
25. Bagian yang agak keliru dari pemahaman lecture
Ada satu hal penting yang perlu diluruskan.
Lecture mengatakan kira-kira bahwa:
“
typeofmengembalikan tipe variable.”
Lebih tepatnya, typeof adalah operator JavaScript yang mengembalikan string yang merepresentasikan kategori runtime value, misalnya "string", "number", atau "object".
Sedangkan TypeScript kemudian menggunakan pengecekan seperti:
typeof value === "string"
sebagai type guard untuk melakukan narrowing.
Jadi:
typeof value
adalah operator JavaScript.
Sedangkan:
typeof value === "string"
dapat berfungsi sebagai TypeScript type guard.
26. Bagaimana dengan custom Type Guard?
Di akhir lecture disebutkan bahwa kita juga bisa membuat custom type guard.
Contohnya nanti kita bisa membuat function seperti:
function isDog(creature: Animal): creature is Dog {
return creature instanceof Dog;
}
Kemudian:
if (isDog(creature)) {
creature.bark();
}
Bagian:
creature is Dog
memberi tahu TypeScript:
“Kalau function ini menghasilkan
true, anggapcreaturesebagaiDog.”
Ini memang sedikit lebih advanced dibanding tiga teknik tadi.
27. Ringkasan keseluruhan lecture
Ada tiga Type Guard utama yang perlu kamu kuasai dulu:
1. typeof
Untuk primitive/simple types:
if (typeof value === "string") {
// value → string
}
Contoh:
string
number
boolean
2. instanceof
Untuk class/object:
if (creature instanceof Dog) {
// creature → Dog
}
3. in
Untuk mengecek keberadaan property:
if ("email" in user) {
// email tersedia
}
28. Cheat sheet
TYPE GUARDS
│
├── typeof
│ └── untuk primitive
│ string
│ number
│ boolean
│
├── instanceof
│ └── untuk class/object
│ Dog
│ Animal
│
└── in
└── untuk mengecek property
"email" in user
Dan konsep besarnya:
Union Type
↓
Type Guard
↓
Type Narrowing
↓
TypeScript tahu tipe yang lebih spesifik
↓
Kita bisa menggunakan property/method
dengan lebih aman
Kalimat yang paling penting untuk diingat
Type Guard adalah cara kita melakukan pengecekan terhadap value sehingga TypeScript bisa mempersempit union/tipe yang sebelumnya masih terlalu umum menjadi tipe yang lebih spesifik.
Kalau kamu sudah memahami typeof → instanceof → in → type narrowing, inti dari lecture ini sebenarnya sudah kamu kuasai.