型別縮小
型別縮小(Narrowing)是 TypeScript 根據程式碼裡的判斷式(if、typeof、in 等),自動把一個較廣的型別(例如 union)在某個範圍內「縮小」成更精確的型別。這是讓 Union 型別 用起來安全又方便的關鍵機制。
typeof:縮小基本型別
function formatValue(value: string | number) {
if (typeof value === "string") {
return value.toUpperCase() // 這裡 TS 知道 value 是 string
}
return value.toFixed(2) // 這裡自動剩下 number
}
typeof value === "string" 這個判斷式本身沒有任何特殊語法,但 TypeScript 會分析程式流程:在 if 區塊裡,value 一定是 string;離開這個區塊,剩下的可能性只有 number。
in:縮小物件形狀
interface Cat { meow(): void }
interface Dog { bark(): void }
function makeSound(animal: Cat | Dog) {
if ("meow" in animal) {
animal.meow() // 縮小成 Cat
} else {
animal.bark() // 縮小成 Dog
}
}
in 檢查物件是否有某個屬性/方法名稱,適合用在兩個型別沒有共同的可判斷欄位、但成員名稱不同的情況。
Discriminated Union:最常見的實務寫法
type LoadingState = { status: "loading" }
type SuccessState = { status: "success"; data: string[] }
type ErrorState = { status: "error"; message: string }
type State = LoadingState | SuccessState | ErrorState
function render(state: State) {
switch (state.status) {
case "loading":
return "Loading..."
case "success":
return `Got ${state.data.length} items` // 這裡自動縮小成 SuccessState
case "error":
return `Error: ${state.message}` // 自動縮小成 ErrorState
}
}
每個型別都有一個共同、但值不同的欄位(這裡是 status,稱為判別欄位/discriminant),switch 或 if 判斷這個欄位後,TypeScript 就能精準縮小到對應的那個型別,抓到「存取到不存在屬性」這種錯誤。這是描述「非同步狀態」「表單驗證結果」這類多種可能狀態時最推薦的寫法。
相關資源
型別縮小是「懂 union 卻不知道怎麼安全使用」最常卡關的地方,目前沒有找到品質夠好、頻道夠可信的獨立教學影片可以嵌入;官方 Handbook 的 Narrowing 一章把每種寫法搭配範例講得很完整,建議直接讀這篇,或在 影音教學 的長篇課程裡找 narrowing/union 段落對照。