在 TypeScript 中键入由继承方法调用的重写方法(错误?)

Typing of overridden method, called by inherited method, in TypeScript (Bug?)

我最近偶然发现了以下场景:

class A {
  public m1(x: string | string[]): string | string[] {
    return this.m2(x);
  }
  protected m2(x: string | string[]): string | string[] {
    return x;
  }
}
class B extends A {
  protected m2(x: string[]): string { // no compiler warning
    return x.join(',');
  }
}
const b = new B();
console.log(b.m1(['a', 'b', 'c'])); // ok
console.log(b.m1('d')); // runtime error

这是 TypeScript 打字系统的错误还是故意的?如果是后者,我如何更改类型以便编译器识别问题?

这是预期的行为。在打字稿中,您重写一个方法来降低方法的特异性,而不是像在其他语言中那样扩大它。

至于说明...

class A {
  public m1(x: string | string[]): string | string[] {
    /**
     * Here variable 'x' is of type string | string[], which perfectly overlaps 
     * with the arguments for the method m2. So you won't get a compile-time error
     */
    return this.m2(x);
  }
  protected m2(x: string | string[]): string | string[] {
    return x;
  }
}
class B extends A {
  /**
   * Here method m2 is overridden by a method which accepts a string[], which
   * is covered by the parent's method, and hence no compile-time error. If
   * you made the argument type to 'string', it would still be accepted.
   */
  protected m2(x: string[]): string { // no compiler warning
    return x.join(',');
  }
}
const b = new B();
console.log(b.m1(['a', 'b', 'c'])); // ok
/**
 * Here you are passing the type you have specified method m1 will take. So there is no issue here as well
 */
console.log(b.m1('d')); // runtime error

综上所述,这是一个 logical-error,而不是一个错误。

在 TypeScript 中,方法是 bivariant and function properties contravariant with --strictFunctionTypes。注意它们不同的语法:

class MyClass {
  public fn(a: string): string { ... } // this is a method
  public fn = (a: string): string => { ... } // this is a function as property
}

要为 m2 启用更严格的输入,您需要启用 --strictFunctionTypes(默认为 strict)并使用函数 属性:

protected m2 = (x: string | string[]): string | string[] => {
  return x;
}

现在,m2 会正确出错,您需要区分 stringstring[]

protected m2 = (x: string | string[]): string => { 
  return Array.isArray(x) ? x.join(',') : `${x},`;
}

Live code example on Playground

相关:What are covariance and contravariance?

相关:JS class fields