这不是一个很好的做法:子类继承超类的属性和方法。如果不需要继承,则可能真的需要组合而不是类层次结构,但这超出了这个问题的范围。让我们展示如何获得您正在寻找的行为,然后在最后提醒大家这可能会导致奇怪的问题。
如果您想防止编译器将BaseThing
构造函数视为构造具有start
属性的东西,您可以使用类型断言将类型BaseThing
从(类似)扩大new () => BaseThing
到new () => Omit<BaseThing, "start">
使用Omit<T, K>
实用程序类型:
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
继承了除 start
from之外的所有内容BaseThing
。
如果您经常做这种事情,您可以考虑将这种扩展重构为一个辅助函数,例如:
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
astring
而不是其他任何东西。val
更改为某些非类型的伪子类string
将在运行时不愉快,除非它们也覆盖method()
. 因此,任何故意绕过类型安全警告的人都应该注意不要绊倒问题已被扫过的地毯部分。
Playground 代码链接