typeoftipe-lanjutanMenengah3 mnt baca

typeof sebagai Type Query

typeof ganda: di value ia cek tipe runtime, di posisi tipe ia mengambil tipe variabel.

Satu Kata, Dua Pekerjaan

typeof di TypeScript punya dua pekerjaan yang sama sekali beda, tergantung di mana ia berdiri. Di posisi VALUE (kode yang jalan), typeof x === "string" adalah pengecekan runtime warisan JavaScript. Di posisi TYPE (setelah titik dua, di dalam type X =), typeof config adalah type query: "tolong ambilkan tipe dari nilai ini". Dua wajah ini sering membingungkan pemula, tapi justru kombinasi keduanya yang membuat TypeScript ekspresif.

Type Query: Mencuri Tipe dari Nilai

Skenario nyata: kamu punya object konfigurasi, daftar route, atau schema validasi yang ditulis sekali sebagai value. Menulis ulang tipenya manual berarti dua sumber kebenaran yang pasti divergen suatu hari. Type query menghilangkan duplikasi:

typescript
const config = {
  apiUrl: "https://api.example.com",
  timeout: 5000,
  retry: 3,
};

type Config = typeof config;
// { apiUrl: string; timeout: number; retry: number }

function simpanConfig(c: Config): void {
  // ...
}
simpanConfig(config); // OK
simpanConfig({ apiUrl: "x", timeout: 1 });
// Error: Property 'retry' is missing in type
// '{ apiUrl: string; timeout: number; }' but required in type
// '{ apiUrl: string; timeout: number; retry: number; }'.

Ubah config (tambah field, ubah tipe), dan Config ikut berubah otomatis. Single source of truth: value adalah sumbernya, tipe mengikutinya.

Pola Idiomatis: Union dari Daftar Nilai

Pola paling dicintai komunitas: array const + as const + type query untuk membuat union string yang selalu sinkron dengan datanya:

typescript
const METODE_BAYAR = ["tunai", "qris", "transfer"] as const;
type MetodeBayar = (typeof METODE_BAYAR)[number];
// "tunai" | "qris" | "transfer"

function bayar(metode: MetodeBayar): void {
  console.log(`Bayar via ${metode}`);
}

bayar("qris");   // OK
bayar("tunai");  // OK
bayar("kartu");
// Error: Argument of type '"kartu"' is not assignable to
// parameter of type 'MetodeBayar'.

Tanpa as const, typeof METODE_BAYAR hanya menghasilkan string[] dan union-nya melebar jadi string, sehingga validasi hilang. Tiga bagian ini satu paket yang tidak terpisahkan: const array sebagai data runtime (bisa di-loop untuk dropdown UI), as const pengunci literal, type query penghasil union.

Skenario nyata: daftar ini dipakai untuk me-render pilihan metode bayar di UI SEKALIGUS memvalidasi input di fungsi bayar. Tambah "ewallet" ke array, dropdown dan validasi ikut update tanpa sentuh kode lain.

typeof + Fungsi: ReturnType dan Parameter

Type query juga bekerja pada fungsi, membuka pola turunan tipe yang elegan:

typescript
function buatLaporan(bulan: string) {
  return {
    bulan,
    total: 0,
    rincian: [] as string[],
  };
}

type Laporan = ReturnType<typeof buatLaporan>;
// { bulan: string; total: number; rincian: string[] }

const lap: Laporan = buatLaporan("Oktober"); // sinkron otomatis

Dan untuk meng-copy signature fungsi lain:

typescript
declare function fetchUser(id: string): Promise<{ nama: string }>;
type FetchUser = typeof fetchUser;
// (id: string) => Promise<{ nama: string }>

const mockFetch: FetchUser = async (id) => ({ nama: `User ${id}` });

Ini berguna untuk mocking di test atau membungkus fungsi pihak ketiga: tipe mock selalu cocok dengan aslinya.

Contoh Error Nyata: Widening yang Mengecoh

Jebakan klasik: typeof mengambil tipe yang MELEBAR bila value dideklarasikan dengan let:

typescript
let status = "aktif";
type S1 = typeof status; // string, BUKAN "aktif"!

const MODE = "gelap";
type M1 = typeof MODE; // "gelap" (literal, karena const)

// Perbandingan yang gagal diam-diam:
function setMode(m: "gelap" | "terang"): void { /* ... */ }
setMode(MODE);   // OK
setMode(status);
// Error: Argument of type 'string' is not assignable to
// parameter of type '"gelap" | "terang"'.

let status = "aktif" memberi tipe string karena variabel let bisa di-assign ulang, jadi compiler melebarkan literalnya. Solusinya: pakai const, atau anotasi eksplisit let status: "aktif" | "nonaktif" = "aktif", atau as const. Aturan ingatannya: typeof mengambil apa yang compiler YAKINI, bukan apa yang tertulis saat itu.

Catatan teknis: Jangan tertukar dengan narrowing typeof x === "string" di runtime. Cara bedakan cepat: kalau typeof muncul di posisi value (dalam expression, di if), itu runtime check. Kalau muncul di posisi tipe (setelah : type =, di dalam generic), itu type query. Konteks sintaks menentukan, bukan katanya.

Tantangan

Curi tipe dari config

Diberikan object PENGATURAN (tanpa anotasi tipe). Buat type Pengaturan = typeof PENGATURAN, lalu buat fungsi updatePengaturan(patch: Partial<Pengaturan>): Pengaturan yang menggabungkan patch ke PENGATURAN dengan spread dan mengembalikan hasilnya.

typescript
const PENGATURAN = {
  tema: "gelap",
  bahasa: "id",
  notifikasi: true,
};

type Pengaturan = typeof PENGATURAN;

function updatePengaturan(patch: Partial<Pengaturan>): Pengaturan {
  // gabungkan
}

console.log(updatePengaturan({ bahasa: "en" }));