TypeScript 模版字面量型別 (Template Literal Types)
如果你熟悉 JavaScript 的 Template Literals(使用反引號 `\來組合字串),那麼這個 TypeScript 功能你會覺得非常親切。
簡單來說,它允許我們使用「字串插值」的方式來產生新的型別。
基本語法
語法跟 JavaScript 一模一樣,只是用在 type 定義中:
type World = 'World';
// 組合出 "Hello World"
type Greeting = `Hello ${World}`;
這看起來很普通,但當它遇到「聯合型別 (Union Types)」時,魔術就發生了。
強大的「組合」能力
當你在模版中放入一個聯合型別時,TypeScript 會自動幫你產生所有可能的排列組合(笛卡兒積)。
type Color = 'red' | 'blue';
type Size = 'S' | 'M' | 'L';
// 自動產生 2 x 3 = 6 種組合
type ProductSKU = `${Color}-${Size}`;
// 結果: "red-S" | "red-M" | "red-L" | "blue-S" | "blue-M" | "blue-L"
這在定義 CSS 類別名稱、API 路徑等場景超級實用。
內建字串操作工具
TypeScript 還提供了幾個內建工具來處理字串的大小寫轉換:
Uppercase<S>: 全部轉大寫Lowercase<S>: 全部轉小寫Capitalize<S>: 首字大寫Uncapitalize<S>: 首字小寫
實戰:自動產生事件處理器名稱
假設我們有幾個事件名稱 'click' | 'change',我們想要自動產生對應的 onClick 和 onChange 型別。
type EventName = 'click' | 'change' | 'focus';
// 1. 先把首字變大寫:Click, Change, Focus
// 2. 前面加上 on
type HandlerName = `on${Capitalize<EventName>}`;
// 結果:"onClick" | "onChange" | "onFocus"
進階:搭配 infer 做字串解析
我們可以利用模版字面量來「拆解」字串型別,這有點像是針對型別的 Regular Expression。
範例:拆解 CSS 單位
假設我們想限制輸入值必須是 數字 + px 的格式,並且想把數字提取出來。
// 定義一個特定的字串格式:前面是一堆東西,後面接個 "px"
type PixelString = `${string}px`;
let width: PixelString = '100px'; // OK
// let height: PixelString = "100%"; // 錯誤,沒有 px 結尾
// 提取前面的數字部分
type GetValue<T> = T extends `${infer N}px` ? N : never;
type Val = GetValue<'500px'>; // "500"
範例:提取 API 路由參數
這是一個非常高階的應用,我們可以遞迴地解析字串,抓出裡面所有以 : 開頭的參數。
// 遞迴解析路徑
type ExtractParams<Path> = Path extends `${infer Start}:${infer Param}/${infer Rest}`
? Param | ExtractParams<Rest> // 如果中間有參數 (:id/...)
: Path extends `${infer Start}:${infer Param}` // 如果結尾是參數 (:id)
? Param
: never;
type MyPath = '/users/:userId/posts/:postId';
type Params = ExtractParams<MyPath>;
// 結果:"userId" | "postId"
總結
模版字面量型別讓 TypeScript 的型別系統具備了處理字串的能力。
- 基本組合:用
${}來串接字串型別。 - 自動展開:遇到 Union 會自動產生所有排列組合。
- 字串工具:善用
Capitalize等工具來轉換格式。 - 模式比對:搭配
infer可以解析並提取字串結構(如 API 路由解析)。
這讓 TypeScript 不僅能檢查資料「是不是字串」,還能檢查「字串的內容格式對不對」。