这不是很好的做法:子类继承超类的属性和方法。如果不需要继承,可能真的需要composition instead of class hierarchies,但这超出了这个问题的范围。让我们展示如何获得您正在寻找的行为,然后在最后提醒大家这可能会导致奇怪的问题。
如果您想阻止编译器将 BaseThing 构造函数视为构造具有 start 属性的东西,您可以使用 type assertion 将 BaseThing 的类型从(类似于)new () => BaseThing到new () => Omit<BaseThing, "start"> 使用Omit<T, K> utility type:
class Thing extends (BaseThing as new () => Omit<BaseThing, "start">) {
start: Start = new Start(); // no error now
}
const thing = new Thing();
console.log(thing.start.iso.toUpperCase()) // ISO
这看起来有点难看,但它确实有效。编译器将Thing 视为从BaseThing 继承所有除了 start。
如果你经常做这种事情,你可以考虑将这种扩展重构为一个辅助函数,例如:
class Thing extends OmitCtorProp(BaseThing, "start") {
start: Start = new Start(); // still okay
}
在哪里
const OmitCtorProp =
<A extends any[], T extends object, K extends keyof T>(
ctor: new (...a: A) => T, k: K) =>
ctor as new (...a: A) => Omit<T, K>;
可以隐藏在某个库中。
请注意,根据基类的实现,这种故意扩大然后不兼容的重新缩小可能会导致麻烦。想象一下这样的事情:
class BaseOops {
val: string = "a string"
method() {
console.log(this.val.toUpperCase())
}
}
const baseOops = new BaseOops();
baseOops.method(); // "A STRING"
到目前为止一切顺利。但后来:
class SubOops extends OmitCtorProp(BaseOops, "val") {
val: number = 12345;
}
const subOops = new SubOops();
subOops.method(); // RUNTIME ERROR! this.val.toUpperCase is not a function
这会失败,因为基类的 method() 取决于 val 是 string 而不是其他任何东西。将val 更改为某些非string 类型的伪子类在运行时将不满意,除非它们也覆盖method()。因此,任何有意避开类型安全警告的人都应注意不要绊倒问题所在的地毯部分。
Playground link to code