Template Literal Types
Bangun tipe string dinamis seperti `data-${string}` untuk nama event dan kunci API, lengkap dengan error compiler saat string tidak cocok.
Analogi: Stempel dengan Kolom Isian
Bayangkan stempel karet bertuliskan "DITERIMA TANGGAL ____". Bagian "DITERIMA TANGGAL" selalu sama, kolom isian bisa diisi tanggal apa pun, tapi tidak boleh diisi nama orang. Template literal types adalah stempel versi TypeScript: pola string yang sebagian tetap, sebagian bebas, dan compiler menolak string yang tidak mengikuti pola.
Kenapa Template Literal Types Ada?
Kamu pasti sudah kenal template literal di JavaScript: halo-${nama}. TypeScript membawa ide itu ke level tipe. Sebelum fitur ini ada, pilihanmu cuma dua: tipe string yang terlalu longgar (semua string lolos, termasuk yang salah), atau union literal seperti "klik" | "hover" yang harus ditulis satu per satu dan tidak bisa digenerasi.
Template literal types mengisi celah di antaranya: pola string yang fleksibel tapi tetap diperiksa compiler. Kasus klasiknya adalah nama event, kunci cache, dan route API yang punya awalan tetap.
Contoh 1: Mengetik Nama Event dengan Awalan Tetap
Misalkan sistem event-mu mewajibkan semua nama event diawali data-:
type DataEvent = `data-${string}`;
function onEvent(event: DataEvent, handler: () => void) {
// daftarkan handler...
}
onEvent("data-user-login", () => {}); // OK: cocok dengan pola data-*
onEvent("data-order-paid", () => {}); // OKDataEvent menerima string apa pun asal diawali data-. Kamu dapat keamanan pola tanpa menulis ratusan nama event satu per satu.
TypeScript juga menyediakan tipe bawaan untuk mengubah kapitalisasi di dalam pola: Capitalize, Uncapitalize, Uppercase, Lowercase. Contoh populernya adalah tipe handler on + nama event:
type EventHandlerName = `on${Capitalize<string>}`;
const a: EventHandlerName = "onClick"; // OK
const b: EventHandlerName = "onSubmit"; // OKPola ini menolak string yang tidak diawali huruf kapital setelah on, jadi typo seperti "onclick" tertangkap sebelum program jalan.
Contoh 2: Error Nyata Saat String Tidak Cocok Pola
Lanjutkan contoh pertama, tapi kirim nama event yang salah:
type DataEvent = `data-${string}`;
function onEvent(event: DataEvent, handler: () => void) {}
onEvent("click", () => {});Compiler menolaknya dengan pesan yang sangat jelas:
error TS2345: Argument of type '"click"' is not assignable to parameter of type '`data-${string}`'.
Perhatikan pesannya: TypeScript menampilkan polanya secara harfiah, lengkap dengan backtick. Ini jauh lebih membantu daripada error tipe string biasa karena kamu langsung tahu pola mana yang dilanggar. Pesan seperti ini juga muncul kalau kamu salah kapitalisasi, misalnya mengirim "onclick" ke tipe on${Capitalize<string>}:
type EventHandlerName = `on${Capitalize<string>}`;
const c: EventHandlerName = "onclick";error TS2322: Type '"onclick"' is not assignable to type '`on${Capitalize<string>}`'.
Catatan teknis: Template literal types dievaluasi sepenuhnya saat compile time dan hilang total di JavaScript hasil compile. Artinya pola ini tidak bisa dipakai untuk validasi runtime: kalau string datang dari input user atau API eksternal, kamu tetap butuh pengecekan manual (misalnya
startsWith("data-")) sebelum memperlakukannya sebagaiDataEvent. Tipe ini menjaga kode yang kamu tulis, bukan data yang datang dari luar.
Kesalahan Umum
1. Mengira pola berlaku juga saat runtime
// SALAH: percaya compiler akan menolak data dari API
declare const dariApi: string;
onEvent(dariApi, () => {}); // lolos compile, tapi bisa crash saat jalan
// BENAR: validasi dulu di runtime
if (dariApi.startsWith("data-")) {
onEvent(dariApi as `data-${string}`, () => {});
}2. Lupa bahwa bagian ${string} juga menerima string kosong
type DataEvent = `data-${string}`;
// SALAH: mengira "data-" saja akan ditolak
const e: DataEvent = "data-"; // OK menurut compiler! string kosong tetap string.
// BENAR: kalau awalan saja tidak boleh, tambahkan validasi runtime
// atau gunakan pola yang lebih ketat sesuai kebutuhan.3. Mencampuradukkan dengan union literal biasa
// SALAH: menulis semua kemungkinan satu per satu, tidak scalable
type Event = "data-login" | "data-logout" | "data-register"; // dan 97 lagi...
// BENAR: satu pola menggantikan ratusan literal
type Event = `data-${string}`;Tantangan
Ketik Route API dengan Pola
Definisikan tipe ApiRoute sebagai template literal type berpola /api/v1/${string}. Buat fungsi fetchRoute(route: ApiRoute) lalu panggil dengan /api/v1/users (harus lolos) dan /users (harus gagal dengan error TS2345). Tulis ulang pesan error yang muncul.