TypeScript 型別斷言 (Type Assertion)
有時候,你會比 TypeScript 編譯器更清楚某個值的型別。 這時候你可以使用 型別斷言 來告訴編譯器:「相信我,我知道我在做什麼」。
這就像是拿假證件給夜店的保鑣看,編譯器會放行,但如果執行時出錯,後果自負。
基本語法
1. as 語法 (推薦)
這是最常見的寫法,意圖清晰。
const someValue: unknown = 'this is a string';
// 告訴 TS:把這個 checkedValue 當成 string 來看待
const strLength = (someValue as string).length;
2. 角括號語法 (不推薦)
這是舊式寫法,容易跟 React JSX 語法搞混,所以盡量少用。
const someValue: unknown = 'this is a string';
const strLength = (<string>someValue).length;
satisfies 運算符 (TS 4.9+)
在 TypeScript 4.9 引入了一個非常強大的關鍵字:satisfies。它與 as 斷言不同,satisfies 不會改變值的型別,而是驗證該值是否符合某個型別。
為什麼需要它?
假設我們有一個設定檔,我們希望它符合 Config 這張 interface,但又想保留該物件具體的屬性型別(Type Narrowing)。
問題:使用型別註解 (Type Annotation) 會導致型別被「寬化」(Widen)
type Colors = 'red' | 'green' | 'blue';
type RGB = [number, number, number];
const palette: Record<Colors, string | RGB> = {
red: [255, 0, 0],
green: '#00ff00',
blue: [0, 0, 255],
};
// ❌ 錯誤!TS 只知道 red 是 string | RGB
// 它不確定 red 是不是陣列,所以不讓你用 array 方法
// palette.red.map(x => x * 2);
解決方案:使用 satisfies
// ✅ 使用 satisfies
const palette = {
red: [255, 0, 0],
green: '#00ff00',
blue: [0, 0, 255],
} satisfies Record<Colors, string | RGB>;
// 🙆♂️ TS 現在知道 palette.red 是一個陣列 [number, number, number]
palette.red.map((x) => x * 2);
// 🙆♂️ TS 也知道 palette.green 是一個字串
palette.green.toUpperCase();
這就是 satisfies 強大的地方:它確保了型別安全(如果寫錯屬性會報錯),同時保留了最精確的推斷型別。
常見使用場景
1. DOM 元素操作
TypeScript 不可能知道你的 HTML 結構長怎樣,所以 document.getElementById 通常回傳寬泛的 HTMLElement | null。
如果你確定那個 ID 是一個 input,你就需要斷言。
// TS 只知道它是 HTMLElement,不知道它有 .value 屬性
const input = document.getElementById('search-input');
// input.value; // 錯誤!
// ✅ 使用斷言
const myInput = document.getElementById('search-input') as HTMLInputElement;
myInput.value = 'Hello'; // OK
2. 處理 API 回傳資料
當你從 API 抓資料回來時,通常是 any 或 unknown。你需要斷言它符合你的 Interface。
interface User {
name: string;
age: number;
}
async function getUser() {
const response = await fetch('/api/user');
const data = await response.json(); // data 是 any
return data as User; // 斷言它是 User
}
危險操作:雙重斷言 (Double Assertion)
TS 會阻止你做「不可能」的斷言。
const x = 'hello';
// const y = x as number; // 錯誤!TS 知道 string 不可能是 number
但如果你真的很想強行轉換 (非常不建議),你可以先斷言成 unknown (萬用型別),再斷言成你要的型別。這就是雙重斷言。
// 強行把 string 當 number 用
const y = x as unknown as number;
警告:99% 的情況下你都不應該這樣做。這代表你的程式邏輯可能出了大問題。
非空斷言 (Non-null Assertion)
當你確定某個值絕對不是 null 或 undefined,可以用 !。
function liveDangerously(x?: number | null) {
// 告訴 TS:我確信 x 現在一定有值,不要再煩我了
console.log(x!.toFixed(2));
}
總結
as:型別斷言。告訴編譯器「把他當作這個型別」。風險較高,請謹慎使用。satisfies:型別驗證。告訴編譯器「檢查他是否符合這個型別」,但不改變推斷結果。最安全且推薦的配置檔寫法。!:非空斷言。告訴編譯器「這不是 null/undefined」。