MST
星途 面试题库

面试题:TypeScript 类型守卫的高级应用与原理

在一个复杂的 TypeScript 项目中,存在多个相互关联的类型和接口,如 `A`、`B`、`C`,`B` 和 `C` 继承自 `A`。现有一个函数 `handleData`,它接收一个类型为 `A` 的参数,在函数内部需要根据参数实际是 `B` 还是 `C` 来执行不同的逻辑。请使用类型守卫实现 `handleData` 函数,并深入分析类型守卫背后的类型推断机制,以及在复杂类型体系下如何确保类型守卫的正确性和高效性。
15.5万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

1. 实现 handleData 函数

interface A {
    commonProp: string;
}

interface B extends A {
    bProp: number;
}

interface C extends A {
    cProp: boolean;
}

function handleData(data: A) {
    if ('bProp' in data) {
        const bData = data as B;
        // 处理 B 类型数据的逻辑
        console.log(`处理 B 类型数据: ${bData.commonProp}, ${bData.bProp}`);
    } else if ('cProp' in data) {
        const cData = data as C;
        // 处理 C 类型数据的逻辑
        console.log(`处理 C 类型数据: ${cData.commonProp}, ${cData.cProp}`);
    }
}

2. 类型守卫背后的类型推断机制

  • 类型缩小(Type Narrowing):类型守卫的核心机制是类型缩小。当使用诸如 ininstanceof 等类型守卫操作符时,TypeScript 能够根据运行时的检查结果,在特定代码块内缩小变量的类型范围。例如,在 if ('bProp' in data) 代码块内,TypeScript 知道 data 必然具有 bProp 属性,因此可以安全地将其类型缩小为 B
  • 基于结构的类型推断:TypeScript 是基于结构类型系统的语言。类型守卫通过检查对象的结构(如属性是否存在)来进行类型推断。当 'bProp' in data 为真时,意味着 data 具有 B 类型所要求的结构,所以可以推断为 B 类型。

3. 在复杂类型体系下确保类型守卫的正确性和高效性

  • 正确性
    • 明确类型定义:确保接口和类型定义准确反映业务需求。在复杂项目中,清晰的类型层次结构有助于正确编写类型守卫。例如,如果 BC 有共同的基类 A,确保 A 定义了所有公共属性,避免在类型守卫中误判。
    • 全面检查:对所有可能的子类型进行检查。在上述例子中,不仅要检查 BC 的特有属性,对于其他可能的子类型也要有相应的处理逻辑,避免遗漏。
  • 高效性
    • 避免重复检查:尽量避免在多个类型守卫中重复检查相同的条件。例如,如果有多个条件依赖于某个公共属性的存在,只检查一次并复用结果。
    • 优化顺序:按照可能性高低排列类型守卫条件。如果在实际应用中,B 类型出现的频率远高于 C 类型,将 if ('bProp' in data) 放在前面,减少不必要的检查。