这不是 TypeScript 中的错误,而是有利于静态继承的设计决策。
根据microsoft/TypeScript#4628 中的this comment,对于类的静态方面显然有两个不兼容的用例。一组用户希望看到 ES6 规范指定的静态继承,这意味着他们应该以与检查实例继承相同的方式进行检查:如果您不能将子类的静态属性替换为期望超类的静态属性,你做错了什么。
另一组人不关心静态方面的可替代性。看起来您属于后者……这对您来说太糟糕了,因为 TypeScript 偏爱前者。如果您查看那个 GitHub 问题,看起来他们正在调查更改它,但它有点失败了。所以现在,就是这样。
那么你能做什么(除了解决那个问题并给它一个?)?如果您只关心声明,例如在.d.ts 文件中,那么您可以执行标准库所做的事情并分别描述类的接口和静态方面,而无需使用class。 (例如,查看the Array class is typed 与Array<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) {
}
}
这符合预期:
const a = new A();
a.a;
A.foo("a");
const b = new B();
b.a;
b.b;
B.foo("a"); // error!
请注意,这两种解决方案都没有真正改变运行时发生的任何事情;仍然存在静态继承。所有这些解决方案都是让 TypeScript 忽略任何此类继承。如果您需要支持非覆盖静态属性的继承(例如,如果A 有一个静态bar() 方法而B 没有,您是否希望能够编写B.bar()?)它可以做到但写出来会更复杂。无论如何,希望你现在有一些前进的道路。
Playground link to code