Template Literal Types
Menyusun tipe string dari potongan literal dan union (`on${Event}`), memakai Uppercase/Lowercase/Capitalize/Uncapitalize, dan pola praktis untuk rute, nama event, dan kelas CSS.
Template literal, sekarang di dunia tipe
Kamu sudah tahu template literal di JavaScript: `Halo ${nama}`. TypeScript membawa sintaks yang sama ke dunia tipe untuk membuat tipe string baru dari potongan-potongan.
type Salam = `Halo ${string}`
const a: Salam = "Halo Dunia" // ✅
const b: Salam = "Hai Dunia" // ❌ Type '"Hai Dunia"' is not assignable to type '`Halo ${string}`'
Tipe Salam artinya "string apa pun yang diawali Halo ". Berguna untuk memvalidasi bentuk teks sejak kompilasi.
Mengalikan union
Kekuatan sebenarnya muncul saat bagian dalam ${ } berupa union: TypeScript menghasilkan semua kombinasi.
type Sisi = "atas" | "bawah" | "kiri" | "kanan"
type Margin = `margin-${Sisi}`
// "margin-atas" | "margin-bawah" | "margin-kiri" | "margin-kanan"
type Ukuran = "kecil" | "besar"
type Warna = "merah" | "biru"
type KelasTombol = `btn-${Ukuran}-${Warna}`
// 2 × 2 = 4 kombinasi: "btn-kecil-merah" | "btn-kecil-biru" | "btn-besar-merah" | "btn-besar-biru"
Tidak perlu menulis 4 (atau 40) kombinasi tangan — dan tidak ada yang terlewat.
Utility untuk huruf
| Tipe | Contoh | Hasil |
|---|---|---|
Uppercase<S> | Uppercase<"halo"> | "HALO" |
Lowercase<S> | Lowercase<"HALO"> | "halo" |
Capitalize<S> | Capitalize<"halo"> | "Halo" |
Uncapitalize<S> | Uncapitalize<"Halo"> | "halo" |
Pola 1: nama event
type Aksi = "klik" | "hover" | "fokus"
type NamaHandler = `on${Capitalize<Aksi>}` // "onKlik" | "onHover" | "onFokus"
type Handler = { [K in NamaHandler]?: () => void }
const h: Handler = { onKlik: () => console.log("diklik") }
const salah: Handler = { onKlikk: () => {} } // ❌ typo ketahuan
Pola 2: rute dan URL
type Metode = "GET" | "POST"
type Rute = `/${string}`
function panggil(metode: Metode, rute: Rute) { /* ... */ }
panggil("GET", "/pengguna") // ✅
panggil("GET", "pengguna") // ❌ harus diawali "/"
type Endpoint = `${Metode} ${Rute}` // "GET /…" | "POST /…"
Pola 3: id bertipe
type IdPengguna = `user_${number}`
type IdPesanan = `ord_${number}`
const u: IdPengguna = "user_42" // ✅
const o: IdPesanan = "user_42" // ❌ tidak boleh tertukar
Menarik informasi dengan infer
Dikombinasikan dengan conditional type, template literal bisa mem-parsing string di level tipe:
type AmbilNama<S> = S extends `halo-${infer N}` ? N : never
type N1 = AmbilNama<"halo-budi"> // "budi"
type N2 = AmbilNama<"hai-budi"> // never
type Ekstensi<F> = F extends `${string}.${infer E}` ? E : ""
type E1 = Ekstensi<"foto.png"> // "png"
Pustaka seperti router bertipe memakai teknik ini untuk memahami "/pengguna/:id" dan menghasilkan tipe parameter { id: string }.
Kapan memakai?
- Nama kelas/properti/event yang mengikuti pola tetap (BEM,
onX,getX). - Memvalidasi format string sederhana (diawali, diakhiri, mengandung).
- Menghindari
stringlonggar yang menampung typo.
Jangan terlalu jauh: jumlah kombinasi bisa meledak (3 × 4 × 5 × 6 = 360 anggota union) dan memperlambat editor. Untuk pola longgar gunakan ${string} alih-alih menyebutkan semua kemungkinan.
Kesalahan umum pemula
- Memakai template literal type untuk validasi runtime. Ia hanya memeriksa saat kompilasi; input dari pengguna tetap harus divalidasi.
- Lupa membungkus union dengan
Capitalize<>saat ingin huruf kapital di awal.