隐藏超类方法的静态方法的打字稿绑定

时间:2020-10-14 14:05:20

标签: typescript overloading static-methods

在Javascript中,类继承了静态方法,但是子类方法隐藏具有相同名称的超类方法,而不是重载它们。因此,您可以执行以下操作:

<div>
<img width="150px" height="150px" src="https://img.freepik.com/free-vector/doctor-character-background_1270-84.jpg?size=338&ext=jpg">
</div>

您可以调用A.foo(a)或B.foo(a,b),它将按预期运行。不幸的是Typescript不喜欢那样,它似乎假定与实例方法应用相同的规则,即所有方法都是虚拟的。因此,尝试像这样在.d.ts文件中表示以上内容:

class A {
    static foo(a) { ... }
}
class B extends A {
    static foo(a, b) { ... }
}

会导致错误,因为函数签名不兼容。我可以通过在export class A { static foo(a: string): void } export class B extends A { static foo(a: string, b: number): void } 中复制A的{​​{1}}声明和foo的声明来解决此问题(.d.ts文件也不需要两者都兼容的实现),但这是一个谎言!我是说可以调用B,而实际上这是无效的,并且可能导致不确定的行为。这引入了“类型危险”,与Typescript应该做的相反。我还有其他方法可以在Typescript中正确表示这些方法吗,或者至少像注释这样的东西会在试图调用不正确版本的代码中生成警告?这是Typescript中的错误吗?

1 个答案:

答案 0 :(得分:1)

这不是TypeScript中的错误,而是支持静态继承的设计决策。

根据this comment中的microsoft/TypeScript#4628,类的静态方面显然存在两种不兼容的用例。一组用户希望看到ES6规范指定的静态继承,这意味着应该以检查实例继承的相同方式对其进行检查:如果您不能在期望超类的静态属性的情况下替换子类的static属性,你做错了什么。

另一组人并不关心静态方面的可替换性。看起来您属于后一组...这对您来说太糟糕了,因为TypeScript支持前者。如果您查看该GitHub问题,似乎他们研究了对此进行更改的想法,但失败了。所以现在就这样。


那么您能做什么(除了解决该问题并给它一个?)?如果您只关心声明(例如.d.ts文件中的声明),则可以执行标准库的操作并分别描述类的接口和静态端,而无需使用{{1} }。 (例如,查看如何the Array class is typedclassArray<T>一起使用)。像这样:

ArrayConstructor

您可以测试它是否按预期工作。

declare namespace MyModule {
  export interface A {
    // instance props/methods
  }
  export interface AConstructor {
    new(): A;
    foo(a: string): void;
  }
  export const A: AConstructor;

  export interface B extends A {
    // new pros/methods
  }
  export interface BConstructor {
    new(): B;
    foo(a: string, b: number): void;
  }
  export const B: BConstructor;
}

另一方面,如果您关心带有行为的实际类定义,则可以编写一个类构造函数工厂函数,该函数忽略其类型的静态属性,以使编译器不会尝试强制实现静态替代性:

MyModule.A.foo("a"); // okay
MyModule.B.foo("a"); // error

然后像这样使用它:

function NoInheritStatics<A extends any[], R>(
  ctor: new (...a: A) => R
): new (...a: A) => R {
  return ctor;
}

此行为符合预期:

class A {
  a: string = "a";
  static foo(a: string) {

  }
}

class B extends NoInheritStatics(A) {
  b: string = "b";
  static foo(a: string, b: string) {

  }
}

请注意,这两种解决方案都不能真正改变运行时发生的任何事情。仍然有静态方面的继承。所有这些解决方案所做的就是使TypeScript忽略任何此类继承。如果您需要支持非覆盖静态属性的继承(例如,如果const a = new A(); a.a; A.foo("a"); const b = new B(); b.a; b.b; B.foo("a"); // error! 具有静态A方法,而bar()没有),您是否希望能够编写{{1 }}?)可以完成,但写起来会更复杂。无论如何,希望您现在有前进的路。

Playground link to code