目錄


型別守衛(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


#TypeScript #type guard #type predicate #型別守衛 #型別預測







Related Posts

Day 91

Day 91

ORM 與 Sequelize

ORM 與 Sequelize

RESTful API

RESTful API


Comments