Operator #
Operator adalah simbol atau kata kunci yang memberitahu compiler untuk melakukan operasi tertentu pada satu atau lebih operan. TypeScript mewarisi semua operator JavaScript — tapi interaksi operator dengan sistem tipe TypeScript menciptakan dimensi baru yang penting dipahami. Beberapa operator JavaScript yang terlihat aman ternyata bisa menyembunyikan bug tipe; sebaliknya, TypeScript menambahkan operator khusus seperti as, satisfies, dan keyof yang tidak ada di JavaScript sama sekali. Artikel ini membahas semua operator yang relevan untuk pengembangan TypeScript sehari-hari — dengan penekanan khusus pada jebakan yang sering tidak disadari dan operator unik yang ditambahkan TypeScript.
Operator Aritmatika #
Operator aritmatika bekerja pada tipe number dan bigint. TypeScript memastikan secara statis bahwa operator aritmatika hanya digunakan pada tipe yang kompatibel — kamu tidak bisa secara tidak sengaja menjumlahkan string dengan number seperti di JavaScript.
let a: number = 20;
let b: number = 6;
console.log(a + b); // 26 — penjumlahan
console.log(a - b); // 14 — pengurangan
console.log(a * b); // 120 — perkalian
console.log(a / b); // 3.3333... — pembagian (selalu float di JS/TS)
console.log(a % b); // 2 — modulus (sisa bagi)
console.log(a ** b); // 64_000_000 — eksponen (pangkat)
// Increment dan decrement
let counter = 0;
counter++; // Post-increment: gunakan nilai dulu, lalu tambah
++counter; // Pre-increment: tambah dulu, lalu gunakan nilai
counter--; // Post-decrement
--counter; // Pre-decrement
Separator Numerik untuk Keterbacaan #
TypeScript mendukung underscore sebagai pemisah ribuan dalam literal angka — ini hanya sugar syntax, tidak mengubah nilai:
// ANTI-PATTERN: Angka besar tanpa pemisah — sulit dibaca
const batasTransaksi = 50000000;
const jarakBumi = 149600000000;
// BENAR: Gunakan underscore sebagai pemisah ribuan
const batasTransaksi2 = 50_000_000; // 50 juta
const jarakBumi2 = 149_600_000_000; // 149,6 miliar meter
const satu_kb = 1_024;
const rgb_merah = 0xFF_00_00; // Bisa juga di hex
Jebakan Aritmatika: Nilai Khusus #
TypeScript tidak bisa menangkap semua error aritmatika saat kompilasi — beberapa hanya muncul di runtime:
// Pembagian dengan nol — tidak error, menghasilkan Infinity atau NaN
console.log(10 / 0); // Infinity
console.log(-10 / 0); // -Infinity
console.log(0 / 0); // NaN
// NaN (Not a Number) adalah number di TypeScript — sumber bug yang halus
const hasil: number = NaN; // ✓ Tidak ada error kompilasi
console.log(typeof NaN); // "number" — membingungkan tapi ini faktanya
console.log(NaN === NaN); // false — NaN tidak sama dengan dirinya sendiri!
// BENAR: Periksa NaN dengan isNaN() atau Number.isNaN()
const inputPengguna = parseFloat("bukan-angka");
if (Number.isNaN(inputPengguna)) {
console.log("Input bukan angka yang valid");
}
Operator Penugasan (Assignment) #
Operator penugasan menetapkan nilai ke variabel. TypeScript memastikan tipe nilai yang ditugaskan kompatibel dengan tipe variabel.
let skor: number = 100;
skor += 10; // skor = skor + 10 → 110
skor -= 5; // skor = skor - 5 → 105
skor *= 2; // skor = skor * 2 → 210
skor /= 3; // skor = skor / 3 → 70
skor %= 8; // skor = skor % 8 → 6
skor **= 2; // skor = skor ** 2 → 36
Logical Assignment Operators (ES2021) #
TypeScript mendukung tiga operator penugasan logika yang sangat berguna untuk pattern inisialisasi:
// ??= : Tetapkan hanya jika nilai saat ini null atau undefined
let konfigurasi: string | null = null;
konfigurasi ??= "nilai-default"; // konfigurasi = "nilai-default"
konfigurasi ??= "nilai-lain"; // Tidak berubah — konfigurasi sudah ada nilainya
// ||= : Tetapkan jika nilai saat ini falsy (null, undefined, 0, "", false)
let nama: string = "";
nama ||= "Anonim"; // nama = "Anonim" karena "" adalah falsy
// &&= : Tetapkan hanya jika nilai saat ini truthy
let pesan: string = "Halo";
pesan &&= pesan.toUpperCase(); // pesan = "HALO" karena "Halo" truthy
let pesanKosong: string = "";
pesanKosong &&= pesanKosong.toUpperCase(); // Tidak berubah — "" falsy
Operator Perbandingan: == vs ===
#
Ini adalah salah satu sumber bug paling umum yang diwarisi dari JavaScript. TypeScript memberi keamanan tambahan tapi tidak sepenuhnya mencegah penggunaan ==.
// == (loose equality) — melakukan type coercion sebelum membandingkan
console.log(1 == "1"); // true — string "1" dikonversi ke number 1
console.log(0 == false); // true — false dikonversi ke 0
console.log(null == undefined); // true — kasus khusus
// === (strict equality) — membandingkan nilai DAN tipe, tanpa coercion
console.log(1 === "1"); // false — tipe berbeda, langsung false
console.log(0 === false); // false — tipe berbeda
console.log(null === undefined); // false — tipe berbeda
TypeScript dengan strict: true sering mencegah perbandingan == yang jelas salah tipe, tapi tidak selalu:
// TypeScript mencegah perbandingan yang jelas tidak masuk akal
const angka: number = 42;
// if (angka == "42") {} // ✗ Error: This comparison appears to be unintentional...
// Tapi untuk union type, == masih bisa lolos — tetap gunakan ===
let nilai: number | null = null;
if (nilai == null) { // ✓ Trik lama: == null menangkap null DAN undefined
console.log("Nilai kosong");
}
// Setara dengan: if (nilai === null || nilai === undefined)
Gunakan===untuk hampir semua perbandingan. Satu pengecualian yang diterima adalah== nulluntuk mengecek sekaligusnulldanundefined— tapi banyak tim yang lebih memilih menulis kondisinya secara eksplisit agar lebih jelas.
Tabel Perbandingan Operator Relasional #
| Operator | Arti | Contoh | Hasil |
|---|---|---|---|
== | Sama (dengan coercion) | "5" == 5 | true |
=== | Identik (tanpa coercion) | "5" === 5 | false |
!= | Tidak sama (dengan coercion) | "5" != 5 | false |
!== | Tidak identik (tanpa coercion) | "5" !== 5 | true |
> | Lebih besar | 10 > 5 | true |
< | Lebih kecil | 3 < 7 | true |
>= | Lebih besar atau sama | 5 >= 5 | true |
<= | Lebih kecil atau sama | 4 <= 3 | false |
Operator Logika dan Short-Circuit Evaluation #
Operator logika di TypeScript tidak hanya menghasilkan boolean — mereka mengembalikan salah satu operan berdasarkan short-circuit evaluation. Ini adalah perilaku penting yang sering dimanfaatkan untuk pattern ringkas.
// && (AND) — mengembalikan operan kiri jika falsy, operan kanan jika kiri truthy
console.log(true && "halo"); // "halo" — kiri truthy, kembalikan kanan
console.log(false && "halo"); // false — kiri falsy, kembalikan kiri
console.log(0 && "halo"); // 0 — 0 falsy, kembalikan kiri
console.log("ada" && "halo"); // "halo" — kiri truthy, kembalikan kanan
// || (OR) — mengembalikan operan kiri jika truthy, operan kanan jika kiri falsy
console.log(true || "fallback"); // true — kiri truthy, kembalikan kiri
console.log(false || "fallback"); // "fallback" — kiri falsy, kembalikan kanan
console.log(0 || "fallback"); // "fallback" — 0 falsy, kembalikan kanan
console.log("ada" || "fallback"); // "ada" — kiri truthy, kembalikan kiri
?? — Nullish Coalescing
#
?? hanya mengembalikan sisi kanan jika sisi kiri adalah null atau undefined — berbeda dari || yang mengembalikan sisi kanan untuk semua nilai falsy:
// Perbedaan krusial antara || dan ??
const stok = 0;
// ANTI-PATTERN: Menggunakan || untuk fallback angka
const tampilStok1 = stok || "Tidak ada stok";
// "Tidak ada stok" — SALAH! 0 adalah stok valid, bukan "tidak ada nilai"
// BENAR: Menggunakan ?? untuk fallback null/undefined saja
const tampilStok2 = stok ?? "Data tidak tersedia";
// 0 — BENAR! 0 bukan null/undefined, jadi nilai asli dipertahankan
// Contoh nyata: konfigurasi dengan nilai default
interface Konfigurasi {
timeout?: number; // mungkin undefined
retries?: number;
}
function buatKoneksi(konfig: Konfigurasi): void {
const timeout = konfig.timeout ?? 5000; // Default 5 detik
const retries = konfig.retries ?? 3; // Default 3 kali
// Dengan ||, timeout = 0 akan diganti 5000 — bug halus!
// Dengan ??, timeout = 0 tetap 0 — perilaku yang benar
}
?. — Optional Chaining
#
Optional chaining menghentikan evaluasi dan mengembalikan undefined segera jika operan di kiri ?. adalah null atau undefined:
interface Profil {
pengguna?: {
alamat?: {
kota?: string;
};
};
}
const data: Profil = {};
// ANTI-PATTERN: Pengecekan manual yang verbose
const kota1 =
data.pengguna !== undefined &&
data.pengguna.alamat !== undefined &&
data.pengguna.alamat.kota !== undefined
? data.pengguna.alamat.kota
: undefined;
// BENAR: Optional chaining — ringkas dan aman
const kota2 = data.pengguna?.alamat?.kota; // undefined — tidak crash
// Optional chaining juga bekerja untuk pemanggilan fungsi
const panjangKota = data.pengguna?.alamat?.kota?.length; // undefined
// Dan untuk akses elemen array
const items: string[] | undefined = undefined;
const item = items?.[0]; // undefined — tidak crash
Operator Tipe: typeof, instanceof, dan in
#
Tiga operator ini sangat penting dalam TypeScript karena berfungsi ganda: selain memberikan informasi runtime, mereka juga digunakan untuk type narrowing — menyempitkan tipe union menjadi lebih spesifik dalam blok kondisi.
typeof — Narrowing Tipe Primitif
#
function prosesNilai(nilai: string | number | boolean | null): string {
// typeof bekerja untuk tipe primitif
if (typeof nilai === "string") {
return nilai.toUpperCase(); // TypeScript tahu: nilai adalah string di sini
}
if (typeof nilai === "number") {
return nilai.toFixed(2); // TypeScript tahu: nilai adalah number di sini
}
if (typeof nilai === "boolean") {
return nilai ? "Ya" : "Tidak"; // TypeScript tahu: nilai adalah boolean
}
return "Nilai kosong"; // TypeScript tahu: nilai adalah null di sini
}
// Tabel hasil typeof untuk tipe-tipe umum
// typeof "teks" → "string"
// typeof 42 → "number"
// typeof true → "boolean"
// typeof undefined → "undefined"
// typeof {} → "object"
// typeof [] → "object" ← jebakan! Array adalah "object"
// typeof null → "object" ← bug historis JavaScript
// typeof function(){} → "function"
typeof null === "object"adalah bug historis JavaScript yang tidak bisa diperbaiki karena akan merusak kompatibilitas mundur. Untuk mengecek null, gunakan selalu=== nullsecara eksplisit, bukantypeof.
instanceof — Narrowing Kelas dan Objek
#
instanceof mengecek apakah sebuah object merupakan instance dari kelas tertentu — ia menelusuri prototype chain:
class HewanPeliharaan {
constructor(public nama: string) {}
}
class Kucing extends HewanPeliharaan {
mengeong(): void { console.log("Meow!"); }
}
class Anjing extends HewanPeliharaan {
menggonggong(): void { console.log("Woof!"); }
}
function suaraHewan(hewan: Kucing | Anjing): void {
// instanceof untuk narrowing kelas
if (hewan instanceof Kucing) {
hewan.mengeong(); // TypeScript tahu: hewan adalah Kucing di sini
} else {
hewan.menggonggong(); // TypeScript tahu: hewan adalah Anjing di sini
}
}
// instanceof juga berguna untuk error handling
try {
JSON.parse("invalid");
} catch (error) {
if (error instanceof SyntaxError) {
console.log("JSON tidak valid:", error.message);
} else if (error instanceof Error) {
console.log("Error lain:", error.message);
}
}
in — Narrowing Berdasarkan Properti
#
Operator in mengecek apakah sebuah properti ada dalam sebuah object — sangat berguna untuk narrowing discriminated union:
interface KuciPersegi {
tipe: "persegi";
sisi: number;
}
interface KuciiLingkaran {
tipe: "lingkaran";
jariJari: number;
}
type Bentuk = KuciPersegi | KuciiLingkaran;
function hitungLuas(bentuk: Bentuk): number {
// Narrowing dengan 'in' — cek keberadaan properti spesifik
if ("sisi" in bentuk) {
return bentuk.sisi ** 2; // TypeScript tahu: bentuk adalah KuciPersegi
}
return Math.PI * bentuk.jariJari ** 2; // TypeScript tahu: bentuk adalah KuciiLingkaran
}
Operator Ternary dan Nested Ternary #
Operator ternary adalah ekspresi kondisional satu baris: kondisi ? nilaiBila_true : nilaiBila_false. TypeScript menyimpulkan tipe hasil ternary dari kedua cabangnya:
const usia: number = 17;
// Ternary sederhana — TypeScript menyimpulkan tipe string
const status = usia >= 18 ? "Dewasa" : "Belum dewasa";
// Tipe status: string
// Ternary dengan tipe yang berbeda — TypeScript membuat union
const nilai = usia >= 18 ? 100 : null;
// Tipe nilai: number | null
// ANTI-PATTERN: Nested ternary yang sulit dibaca
const label = usia < 13 ? "Anak" : usia < 18 ? "Remaja" : usia < 60 ? "Dewasa" : "Lansia";
// Sulit dibaca, sulit di-debug
// BENAR: Gunakan fungsi atau if-else untuk kondisi bertingkat
function kategorikanUsia(usia: number): string {
if (usia < 13) return "Anak";
if (usia < 18) return "Remaja";
if (usia < 60) return "Dewasa";
return "Lansia";
}
Operator Bitwise #
Operator bitwise bekerja pada representasi biner 32-bit dari angka. Jarang dipakai dalam pengembangan aplikasi umum, tapi penting untuk pemrosesan data biner, permission flags, dan optimasi performa tertentu.
const a: number = 5; // Biner: 0000 0101
const b: number = 3; // Biner: 0000 0011
console.log(a & b); // AND: 0000 0001 = 1
console.log(a | b); // OR: 0000 0111 = 7
console.log(a ^ b); // XOR: 0000 0110 = 6
console.log(~a); // NOT: 1111 1010 = -6 (two's complement)
console.log(a << 1); // Left shift: 0000 1010 = 10 (× 2)
console.log(a >> 1); // Right shift: 0000 0010 = 2 (÷ 2)
console.log(a >>> 1); // Unsigned right shift: 2 (sama untuk angka positif)
Pola Umum Bitwise: Permission Flags #
// Pattern permission flags — setiap bit mewakili satu izin
const IZIN_BACA = 0b0001; // 1
const IZIN_TULIS = 0b0010; // 2
const IZIN_HAPUS = 0b0100; // 4
const IZIN_ADMIN = 0b1000; // 8
// Gabungkan izin dengan OR
const izinEditor = IZIN_BACA | IZIN_TULIS; // 0011 = 3
const izinAdmin = IZIN_BACA | IZIN_TULIS | IZIN_HAPUS | IZIN_ADMIN; // 1111 = 15
// Cek izin dengan AND
function punyaIzin(izinPengguna: number, izinDicek: number): boolean {
return (izinPengguna & izinDicek) === izinDicek;
}
console.log(punyaIzin(izinEditor, IZIN_BACA)); // true
console.log(punyaIzin(izinEditor, IZIN_HAPUS)); // false
console.log(punyaIzin(izinAdmin, IZIN_ADMIN)); // true
Operator Khusus TypeScript #
Selain operator JavaScript, TypeScript menambahkan beberapa operator yang hanya ada di level type system.
as — Type Assertion
#
as memberitahu TypeScript untuk memperlakukan sebuah nilai sebagai tipe tertentu, mengabaikan inferensi compiler. Ini adalah operasi “percayakan padaku” yang rawan disalahgunakan:
// Kasus sah: menyempitkan tipe yang diketahui lebih spesifik
const inputElemen = document.getElementById("nama") as HTMLInputElement;
console.log(inputElemen.value); // ✓ TypeScript tahu ini HTMLInputElement, bukan HTMLElement
// ANTI-PATTERN: Menyalahgunakan as untuk memaksa tipe yang tidak kompatibel
const angka = "bukan angka" as unknown as number;
// Dua as: pertama ke unknown (lolos), lalu ke number — ini cara "memaksa" assertion
// Tidak ada error kompilasi, tapi logika salah total
satisfies — Validasi Tipe Tanpa Kehilangan Inference (TypeScript 4.9+)
#
satisfies adalah operator yang lebih aman dari as — ia memvalidasi bahwa nilai kompatibel dengan tipe tertentu, tapi tetap mempertahankan tipe paling spesifik yang disimpulkan compiler:
type PaletWarna = {
[kunci: string]: string | [number, number, number];
};
// Dengan anotasi tipe biasa — tipe spesifik hilang
const palet1: PaletWarna = {
merah: [255, 0, 0],
hijau: "#00FF00",
};
// palet1.merah — tipe: string | [number, number, number]
// Tidak bisa langsung pakai .length pada tuple
// Dengan satisfies — tipe spesifik tetap ada
const palet2 = {
merah: [255, 0, 0],
hijau: "#00FF00",
} satisfies PaletWarna;
// palet2.merah — tipe: [number, number, number] — TypeScript tahu ini tuple!
// palet2.hijau — tipe: string — TypeScript tahu ini string!
palet2.merah[0]; // ✓ Akses elemen tuple langsung
palet2.hijau.toUpperCase(); // ✓ Method string langsung
keyof dan typeof di Level Tipe
#
TypeScript menggunakan typeof dan keyof juga sebagai type operators — berbeda dari operator runtime:
const konfigurasi = {
host: "localhost",
port: 5432,
ssl: false,
};
// typeof di level tipe — ekstrak tipe dari nilai
type TipeKonfigurasi = typeof konfigurasi;
// { host: string; port: number; ssl: boolean }
// keyof — ekstrak union dari semua kunci tipe
type KunciKonfigurasi = keyof TipeKonfigurasi;
// "host" | "port" | "ssl"
// Kombinasi yang sangat berguna
function ambilKonfigurasi<K extends keyof typeof konfigurasi>(
kunci: K
): typeof konfigurasi[K] {
return konfigurasi[kunci];
}
const host = ambilKonfigurasi("host"); // Tipe: string
const port = ambilKonfigurasi("port"); // Tipe: number
// ambilKonfigurasi("invalid"); // ✗ Error — kunci tidak ada
Urutan Prioritas Operator #
Operator dievaluasi berdasarkan urutan prioritas — operator dengan prioritas lebih tinggi dievaluasi lebih dulu. Tabel berikut diurutkan dari prioritas tertinggi ke terendah:
| Prioritas | Operator | Contoh |
|---|---|---|
| Tertinggi | () grouping | (a + b) * c |
?. optional chaining | obj?.prop | |
** eksponen | 2 ** 10 | |
!, ~, ++, --, typeof | !aktif, typeof x | |
*, /, % | a * b / c | |
+, - | a + b | |
<<, >>, >>> | a << 2 | |
<, >, <=, >=, in, instanceof | a > b | |
==, !=, ===, !== | a === b | |
& bitwise AND | a & b | |
^ bitwise XOR | a ^ b | |
| bitwise OR | a | b | |
&& logical AND | a && b | |
|| logical OR | a || b | |
?? nullish coalescing | a ?? b | |
?: ternary | a ? b : c | |
| Terendah | =, +=, -=, ??=, dll | a = b |
Ringkasan #
- Selalu gunakan
===bukan==— strict equality tidak melakukan type coercion dan jauh lebih prediktabel; pengecualian tunggal yang diterima adalah== nulluntuk menangkap null dan undefined sekaligus.??lebih presisi dari||untuk fallback — nullish coalescing hanya menggantikannulldanundefined, sementara||menggantikan semua nilai falsy termasuk0,"", danfalseyang sering merupakan nilai valid.?.untuk akses aman properti bertingkat — optional chaining menghilangkan kebutuhan pengecekan null manual yang verbose dan mencegah TypeError saat runtime.typeof,instanceof,inbukan hanya runtime operator — di TypeScript, ketiganya juga berfungsi sebagai type guard yang menyempitkan tipe union dalam blok kondisi.typeof null === "object"adalah bug historis — selalu cek null dengan=== null, jangan dengantypeof.??=,||=,&&=untuk inisialisasi ringkas — logical assignment operators menggabungkan operasi cek dan penugasan dalam satu ekspresi yang lebih ekspresif.satisfieslebih aman darias— gunakansatisfiesuntuk memvalidasi tipe sambil mempertahankan inference yang spesifik;asharus dianggap sebagai jalan terakhir karena ia mengabaikan pemeriksaan compiler.keyof typeofadalah kombinasi powerful — ekstrak union tipe kunci dari sebuah nilai secara dinamis, berguna untuk membuat fungsi yang type-safe dengan kunci yang tidak hardcoded.- Operator bitwise untuk permission flags — pola
flags & FLAG_Xadalah cara efisien mengelola izin berlapis dalam satu angka integer.