TypeScript 字面量型別 (Literal Types)
除了 string 和 number 這種寬泛的型別,TypeScript 還允許你把「特定的一個值」當成型別。
這聽起來很廢:「如果變數只能存一個值,那它跟常數有什麼兩樣?」 沒錯!但當它搭配 聯合型別 (Union Types) 一起使用時,就會變得超級強大。
單一字面量型別
這種宣告其實意義不大,因為它就被鎖死了。
let x: 'hello' = 'hello';
// x = "world"; // 錯誤!x 只能是 "hello"
聯合字面量型別 (實用!)
這才是它真正的用法:用來限制一組特定的合法值。這基本上就是現代版的 Enum。
// 變數只能是這三個字串之一
type Alignment = 'left' | 'center' | 'right';
function setAlign(align: Alignment) {
// ...
}
setAlign('left'); // OK
setAlign('center'); // OK
// setAlign("top"); // 錯誤!"top" 不是合法的 Alignment
這比 Enum 好的地方在於:
- 所見即所得:值就是字串,debug 時清楚明瞭。
- 零負擔:編譯後完全消失,沒有額外的 JavaScript 程式碼。
數字與布林字面量
除了字串,數字和布林值也可以這樣玩。
type Dice = 1 | 2 | 3 | 4 | 5 | 6;
type HttpSuccess = 200 | 201;
function rollDice(): Dice {
// return 7; // 錯誤!
return 1;
}
const vs let 的型別推論
這是一個容易踩雷的地方。
當你用 var 或 let 宣告變數時,TS 會把它推論為寬泛的型別 (string, number)。
但如果你用 const,TS 會把它推論為字面量型別。
// let: 值可能會變,所以型別是 string
let s = 'Hello';
// const: 值永遠不會變,所以型別是 "Hello" (字面量)
const c = 'Hello';
物件屬性的陷阱
const req = { url: 'https://example.com', method: 'GET' };
// 這裡 req.method 被推論為 string (因為物件屬性是可以被修改的)
handleRequest(req.url, req.method);
function handleRequest(url: string, method: 'GET' | 'POST') {
// ...
}
上面程式碼會報錯,因為 req.method 是 string,可以被改成隨意字串,不符合 "GET" | "POST" 的限制。
解法:as const
加上 as const,告訴 TS 說「這個物件的所有屬性都是唯讀的字面量」。
const req = { url: 'https://example.com', method: 'GET' } as const;
// 現在 req.method 的型別就是 "GET" 了
handleRequest(req.url, req.method); // OK