Literal type adalah tipe yang hanya punya tepat satu nilai: "up" adalah tipe yang satu-satunya member-nya adalah string "up", dan 404 adalah tipe yang satu-satunya member-nya adalah angka 404. Kalau berdiri sendiri, literal type tidak terlalu berguna. Kalau digabung menjadi union, kamu mendapat variabel yang hanya menerima sekumpulan nilai tertentu dan tidak ada yang lain.
Tanpa komentar @ts-expect-error, pemanggilan terakhir adalah error saat kompilasi (TS2345). Dengan komentar itu, program lolos kompilasi dan pemanggilan tersebut tetap berjalan serta mencetak moving north by 1: literal type hanya ada untuk compiler, dan saat runtime nilainya adalah string biasa.
Literal String, Angka, dan Boolean
Nilai string, angka, bigint, atau boolean apa pun bisa ditulis sebagai tipe. Compiler lalu hanya menerima nilai yang persis sama.
let method: "GET" = "GET";
let code: 404 = 404;
let ok: true = true;
let big: 10n = 10n;
type Port = 80 | 443 | 8080;
const port: Port = 443;
boolean sendiri hanyalah union true | false, dan karena itulah narrowing sebuah boolean dengan if (flag) menyisakan false di cabang else.
| Literal type | Mengizinkan | Tipe yang lebih lebar |
|---|---|---|
"GET" | hanya string "GET" | string |
404 | hanya angka 404 | number |
10n | hanya bigint 10 | bigint |
true | hanya true | boolean |
Union dari Literal
Pemakaian yang umum adalah union yang mencantumkan setiap nilai yang diizinkan. Di dalam fungsi, compiler mempersempit union itu seiring pemeriksaanmu, jadi setiap cabang tahu persis nilai mana yang sedang dipegangnya.
Union dari string literal adalah alternatif TypeScript yang umum untuk enum. Ia tidak memakan biaya apa pun saat runtime, nilainya adalah string biasa yang bisa kamu log dan kirim lewat JSON, dan salah ketik menjadi error kompilasi. Untung ruginya dibahas lebih jauh di enum.
Widening: let vs const
Saat TypeScript menyimpulkan tipe dari sebuah literal, ia melihat apakah nilainya bisa berubah. Variabel const tidak pernah bisa di-assign ulang, jadi tipe literalnya dipertahankan. Variabel let bisa, jadi tipenya dilebarkan menjadi tipe yang umum.
Arahkan kursor ke setiap nama di editor untuk melihat tipe hasil inferensinya. Kalau kamu ingin let yang hanya berisi nilai tertentu, beri anotasi: let mode: "light" | "dark" = "light".
Kenapa Properti Object Dilebarkan
Properti dari object literal bisa diubah, jadi tipenya juga dilebarkan, bahkan ketika object-nya disimpan di const. Inilah cara paling umum orang tidak sengaja berurusan dengan literal type:
Compiler melaporkan:
index.ts(7,15): error TS2345: Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.
req disimpulkan sebagai { url: string; method: string } karena kode di bagian lain bisa saja menjalankan req.method = "DELETE". Ada tiga cara memperbaikinya:
Pilihan keempat adalah satisfies, yang memeriksa object terhadap sebuah tipe sambil mempertahankan tipe literal propertinya.
as const
as const adalah const assertion. Letakkan setelah sebuah ekspresi, dan compiler akan menyimpulkan tipe paling sempit yang bisa didapat:
- nilai string, angka, dan boolean mempertahankan tipe literalnya
- properti object menjadi
readonly - array literal menjadi tuple
readonlydengan panjang tetap
Assertion ini hanya berlaku saat kompilasi. JavaScript yang dihasilkan adalah object literal yang sama dengan as const dihapus, jadi tidak ada yang mencegah kode lain mengubahnya saat runtime. Kalau kamu butuh jaminan saat runtime, panggil juga Object.freeze.
Union Type dari Array as const
Pola yang sering dipakai adalah menyimpan nilai-nilai yang diizinkan dalam satu array, yang bisa kamu loop saat runtime, lalu menurunkan union type darinya. (typeof arr)[number] berarti "tipe dari elemen mana pun di arr".
Tanpa as const, ROLES akan bertipe string[] dan Role hanya string biasa. Cast ke readonly string[] di isRole dibutuhkan karena includes pada tuple berisi literal hanya menerima literal-literal itu, padahal tujuan fungsinya adalah menguji string yang belum tentu salah satunya. Pola yang sama dengan object berlaku untuk map key/value: const Status = { Active: "active", Banned: "banned" } as const, lalu type Status = (typeof Status)[keyof typeof Status].
Parameter Tipe const
Fungsi generic biasanya melebarkan literal yang kamu kirim. Sejak TypeScript 5.0, kamu bisa menandai parameter tipe dengan const, yang membuat compiler menyimpulkan argumen seolah-olah ditulis dengan as const, tanpa meminta pemanggil menuliskannya.
Ini terutama alat untuk pembuat library: definisi route, builder, dan helper schema memakainya agar pemanggil mendapat tipe yang presisi dari literal biasa.
Arti-arti const
Kata kunci const muncul di empat tempat yang berbeda di kode TypeScript:
| Sintaks | Jenis | Fungsinya |
|---|---|---|
const x = 1 | deklarasi JavaScript | binding tidak bisa di-assign ulang; nilai literal mempertahankan tipe literalnya |
expr as const | assertion TypeScript | tipe paling sempit: literal, properti readonly, tuple readonly |
function f<const T>() | parameter tipe TypeScript | menyimpulkan argumen seolah-olah ditulis dengan as const |
const enum E {} | enum TypeScript | enum yang member-nya disisipkan langsung saat kompilasi |
Tidak satu pun dari mereka membekukan object saat runtime. const obj = { a: 1 } tetap mengizinkan obj.a = 2; hanya meng-assign ulang obj itu sendiri yang menjadi error.
Pertanyaan yang sering diajukan
Apa itu literal type di TypeScript?
Tipe yang hanya mengizinkan tepat satu nilai. "GET" adalah tipe yang satu-satunya nilainya adalah string "GET", 404 adalah tipe yang satu-satunya nilainya adalah angka 404, dan true adalah tipe yang satu-satunya nilainya adalah true. Literal type paling berguna saat digabung menjadi union, seperti type Method = "GET" | "POST".
Apa fungsi as const di TypeScript?
as const adalah const assertion. Ia menyuruh compiler menyimpulkan tipe yang paling sempit untuk sebuah ekspresi: nilai string dan angka mempertahankan tipe literalnya, properti object menjadi readonly, dan array literal menjadi tuple readonly. Yang berubah hanya tipenya; nilainya saat runtime tetap object atau array biasa yang sama, dan tidak dibekukan.
Kenapa TypeScript menyimpulkan string, bukan literal yang saya tulis?
Karena nilainya bisa diubah. let x = "a" dan properti di { method: "GET" } bisa di-assign ulang nanti, jadi TypeScript melebarkannya menjadi string. Variabel const mempertahankan tipe literal "a". Untuk mempertahankan literal di dalam object, beri anotasi dengan tipe literalnya, pakai as const, atau pakai satisfies.
Apa perbedaan const dan as const?
const adalah deklarasi JavaScript: variabelnya tidak bisa di-assign ulang, tetapi object yang ditunjuknya masih bisa diubah. as const adalah type assertion TypeScript: ia membuat setiap properti nilai itu menjadi readonly dan literal di sistem tipe. Tidak satu pun dari keduanya membekukan object saat runtime; pakai Object.freeze untuk itu.
Bagaimana cara mendapatkan union type dari array string?
Deklarasikan array dengan as const, lalu beri indeks number pada tipenya: const roles = ["admin", "user"] as const; type Role = (typeof roles)[number]; menghasilkan "admin" | "user". Tanpa as const, array-nya bertipe string[] dan hasilnya hanya string.