如何实现 TypeScript 的类型守卫?

描述 TypeScript 中实现类型守卫的几种方法及其应用场景。

TypeScript 中等 类型守卫 类型系统

类型守卫(Type Guards)是 TypeScript 中的一种机制,用于在运行时检查变量类型以缩小其类型范围,从而提升类型安全性和代码智能提示功能。以下是实现类型守卫的几种主要方法:

  1. 使用 typeof 守卫(检查基础类型)
    适用于判断变量是否属于 JavaScript 内置基础类型(如 string、number)。
    function padLeft(value: string, padding: string | number) {
        if (typeof padding === "number") {
            return " ".repeat(padding) + value; // padding 被缩小为 number
        }
        if (typeof padding === "string") {
            return padding + value; // padding 被缩小为 string
        }
        throw new Error("期望输入是 number 或 string 类型");
    }
    
  2. 使用 instanceof 守卫(检查类实例类型)
    用于验证对象是否属于特定类的实例,常在类继承体系中应用。
    class Animal {}
    class Dog extends Animal { bark() {} }
    class Cat extends Animal { meow() {} }
    function makeSound(pet: Animal) {
        if (pet instanceof Dog) {
            pet.bark(); // pet 被缩小为 Dog
        } else if (pet instanceof Cat) {
            pet.meow(); // pet 被缩小为 Cat
        }
    }
    
  3. 使用 in 操作符守卫(检查对象属性)
    通过检查属性存续性来区分对象类型,特别适合接口类型联合的场景。
    interface Cat { meow: () => void; }
    interface Dog { bark: () => void; }
    function animalNoise(animal: Cat | Dog) {
        if ("meow" in animal) {
            animal.meow(); // animal 被缩小为 Cat
        } else {
            animal.bark(); // animal 被缩小为 Dog
        }
    }
    
  4. 使用可辨识联合类型(基于判别器属性)
    需为联合类型成员定义一个共同的字面量属性(如 kind),然后用控制流语法(switch)守卫类型。
    type Shape = 
      | { kind: "circle"; radius: number } 
      | { kind: "square"; size: number };
    function getArea(shape: Shape) {
        switch (shape.kind) {
            case "circle": 
                return Math.PI * shape.radius ** 2; // shape 被缩小为圆
            case "square": 
                return shape.size ** 2; // shape 被缩小为方
        }
    }
    
  5. 使用用户定义类型守卫(自定义逻辑)
    创建自定义函数,返回 parameter is Type 的类型谓词表达式以执行复杂检查。
    interface Fish { swim: () => void; }
    interface Bird { fly: () => void; }
    function isFish(pet: Fish | Bird): pet is Fish { // 守卫表达式
        return (pet as Fish).swim !== undefined; 
    }
    function handlePet(pet: Fish | Bird) {
        if (isFish(pet)) {
            pet.swim(); // pet 被缩小为 Fish
        } else {
            pet.fly();  // pet 被缩小为 Bird
        }
    }