MST

星途 面试题库

面试题:TypeScript函数类型注解与类型兼容性对运行时行为的作用

假设有两个接口`A`和`B`,`B`继承自`A`。定义一个函数`func`,它接受一个类型为`A`的参数。现在创建一个类型为`B`的对象,并尝试将其传入`func`。解释这种情况下类型注解的兼容性规则,以及在运行时可能出现的情况,若存在潜在风险,如何在TypeScript层面进行预防?
34.3万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试
  1. 类型注解兼容性规则
    • 在TypeScript中,子类型(这里B继承自AB是子类型,A是父类型)与父类型是兼容的。这意味着一个函数期望接受类型A的参数,那么可以传入类型B的对象。因为B继承自AB拥有A的所有属性和方法,所以从类型系统的角度看,B类型的对象可以安全地当作A类型的对象使用。
  2. 运行时可能出现的情况
    • 如果func函数只依赖于A接口定义的属性和方法,那么运行时不会出现问题。例如,如果A接口定义了methodA方法,func函数调用methodA,而B实现了methodA,那么一切正常。
    • 但是,如果func函数尝试访问B特有的属性或方法(这些属性或方法在A接口中不存在),运行时就会出错。比如B有一个methodB方法,但A没有,func函数中调用methodB就会导致运行时错误,因为在类型系统层面,函数只期望接受A类型,它不知道传入的对象实际上是B类型且有methodB方法。
  3. 在TypeScript层面进行预防
    • 类型断言:如果确定传入的对象是B类型,可以在函数内部进行类型断言。例如:
    interface A {
      methodA: () => void;
    }
    interface B extends A {
      methodB: () => void;
    }
    function func(a: A) {
      if ('methodB' in a) {
        const b = a as B;
        b.methodB();
      }
    }
    const bObj: B = {
      methodA: () => console.log('Method A'),
      methodB: () => console.log('Method B')
    };
    func(bObj);
    
    • 使用类型守卫:也可以通过自定义类型守卫函数来确保对象的类型。例如:
    interface A {
      methodA: () => void;
    }
    interface B extends A {
      methodB: () => void;
    }
    function isB(a: A): a is B {
      return'methodB' in a;
    }
    function func(a: A) {
      if (isB(a)) {
        a.methodB();
      }
    }
    const bObj: B = {
      methodA: () => console.log('Method A'),
      methodB: () => console.log('Method B')
    };
    func(bObj);