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 == null untuk mengecek sekaligus null dan undefined — tapi banyak tim yang lebih memilih menulis kondisinya secara eksplisit agar lebih jelas.

Tabel Perbandingan Operator Relasional #

OperatorArtiContohHasil
==Sama (dengan coercion)"5" == 5true
===Identik (tanpa coercion)"5" === 5false
!=Tidak sama (dengan coercion)"5" != 5false
!==Tidak identik (tanpa coercion)"5" !== 5true
>Lebih besar10 > 5true
<Lebih kecil3 < 7true
>=Lebih besar atau sama5 >= 5true
<=Lebih kecil atau sama4 <= 3false

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 === null secara eksplisit, bukan typeof.

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:

PrioritasOperatorContoh
Tertinggi() grouping(a + b) * c
?. optional chainingobj?.prop
** eksponen2 ** 10
!, ~, ++, --, typeof!aktif, typeof x
*, /, %a * b / c
+, -a + b
<<, >>, >>>a << 2
<, >, <=, >=, in, instanceofa > b
==, !=, ===, !==a === b
& bitwise ANDa & b
^ bitwise XORa ^ b
| bitwise ORa | b
&& logical ANDa && b
|| logical ORa || b
?? nullish coalescinga ?? b
?: ternarya ? b : c
Terendah=, +=, -=, ??=, dlla = b

Ringkasan #

  • Selalu gunakan === bukan == — strict equality tidak melakukan type coercion dan jauh lebih prediktabel; pengecualian tunggal yang diterima adalah == null untuk menangkap null dan undefined sekaligus.
  • ?? lebih presisi dari || untuk fallback — nullish coalescing hanya menggantikan null dan undefined, sementara || menggantikan semua nilai falsy termasuk 0, "", dan false yang 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, in bukan 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 dengan typeof.
  • ??=, ||=, &&= untuk inisialisasi ringkas — logical assignment operators menggabungkan operasi cek dan penugasan dalam satu ekspresi yang lebih ekspresif.
  • satisfies lebih aman dari as — gunakan satisfies untuk memvalidasi tipe sambil mempertahankan inference yang spesifik; as harus dianggap sebagai jalan terakhir karena ia mengabaikan pemeriksaan compiler.
  • keyof typeof adalah 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_X adalah cara efisien mengelola izin berlapis dalam satu angka integer.

← Sebelumnya: Tipe Data   Berikutnya: Seleksi Kondisi →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact