目錄
型別守衛(type guard)是什麼
型別守衛
是TypeScript中的一種技巧
,用於設計時期(design time)區分變數的型別
,以避免在runtime出錯
像是常見的typeof、in、instanceof都算是簡單的型別守衛
使用type guard的優點
是提升type safety
,缺點則是可能讓程式碼變長、難讀
你可能會想問為什麼不直接
暴力用as
就好,因為那會降低
整個code base的type safety
const foo = (data) => {
if('name' in data){
return `Hello, ${data.name}`;
}else{
return data;
}
}
型別守衛中還有另一種叫做型別預測(type predicate)
型別預測(type predicate)
型別預測
是TypeScript 中的一種技巧
,用於在runtime區分變數的型別
具體的做法是定義一個函式
,這個函式會返回一個布林值,表示這個變數是否符合指定的型別
但是需要注意的是雖然說最後返回的是布林值,但回傳值的型別
定義絕對不是boolean,而是要使用
Typescript的is
進行標記
enum Fruit {
Apple = 'Apple',
Banana = 'Banana'
}
// good
const isFruitKey = (f:string):f is keyof typeof Fruit => {
return f in Fruit;
}
// bad
// 這樣的話即使回傳true,型別也不會被判斷為Fruit內的key
const isFruitKey2 = (f:string):boolean => {
return f in Fruit;
}
用起來會像這樣
泛型 & 型別預測
上段的例子只能判斷某個字串是否是enum Fruit的key
但如果要讓型別預測
的函示更泛用
就會需要配合泛型
使用
// 因為這個方法會用在物件上,所以用extends object限縮T的型別為物件
const isKeyOfObject = <T extends object>(enumObject: T) => {
// 因為value是string,所以用Extract限縮value的型別為T的key中是string的那幾個
return (value: string): value is Extract<keyof T, string> => {
return value in enumObject;
}
}
console.log(isKeyOfObject(Fruit)('Apple')); // true
console.log(isKeyOfObject(Fruit)('peach')); // false
判斷某個字串是不是某個enum的value則如下
// 使用Record<string, unknown>而不是object來限縮型別,
// 因為object的話TS會斷言其key可能為string | number | symbol,
// 但是keys為string[],所以一定要限縮T為key為string的物件
const isElementOfObject = <T extends Record<string, unknown>>(enumObject: T) => {
return (value: unknown):value is T[keyof T] => {
const keys = Object.keys(enumObject);
for(let i = 0; i < keys.length - 1; i++){
if(enumObject[keys[i]] === value){
return true;
}
}
return false;
}
};
console.log(isElementOfObject(Fruit)('apple')); // true
console.log(isElementOfObject(Fruit)('peach')); // false
型別斷言函式(assertion function) & 型別預測
斷言函式近似於型別預測,不過在型別不如預期時它會拋出錯誤
// 斷言
const stringAsserting = (value: unknown): asserts value is string => {
if (typeof value !== "string") throw new Error("Not a string");
}
// 預測
const stringGuard = (value: unknown): value is string => {
return typeof value === "string";
}
參考資料
What are Type Predicates in Typescript?
type guard function doesn't work. type still be judged as string
Assertion functions in TypeScript