首页 > 解决方案 > TypeScript :: 继承问题

问题描述

我有一个实现一堆方法的基类,每个方法都返回一个对对象副本的新引用以允许链接。

class Base {
  constructor(public name: string) {}
  funcA(): Base { return new Base('FUNC_A') }
  funcB(): Base { return new Base('FUNC_B') }
}

另外,我有一个派生类,只暴露了基类的一些方法,也暴露了它自己的方法。所有这些方法都应该返回对派生类对象的引用以允许链接。

class Derived extends Base {
  constructor() { super('DERIVED') }
  funcA(): Derived { return super.funcA() }
  newFunc(): Derived { return new Derived() }
}

在这种情况下,我遇到了问题。被覆盖的方法返回的对象仍然是基类的一个实例,不管它是否被强制转换为派生类,派生类中第一次定义的方法都没有被定义。

我想到了一种解决方法,它并不优雅。代替继承,我可以使用组合在派生类对象中包含基类对象的实例。但是,为此,我需要一个额外的构造函数来接受一个基类对象,该对象应该可以在类外部访问,并像funcA(): Derived { return new Derived(this.baseObject) }.

有没有更优雅的方法来解决这个问题?

标签: typescriptinheritancedesign-patternsobject-composition

解决方案


我认为这符合您的预期:

class Base {
  constructor(public name: string) {}
  funcA(): this { return new (this.constructor as any)('FUNC_A') }
  funcB(): this { return new (this.constructor as any)('FUNC_B') }
}

class Derived extends Base {
  constructor() { super('DERIVED') }
  funcA(): this { return super.funcA() }
  newFunc(): this { return new (this.constructor as any)() }
}

解释:

使用funcA(): this而不是funcA(): Base告诉 TypeScript,该函数返回一个与调用它的类相同类型的对象,而不是总是Base. 如果调用它的实例Base应该返回一个实例,Base如果调用Derived它应该返回一个实例Derived

要正确实现这一点,调用new Base()将是错误的,因为它总是返回 base 的实例。要访问实际实例的构造函数,每个 javascript 对象都有一个this.constructor属性(参见:https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor )。所以我打电话new this.constructor()而不是new Base(). 如果在的实例上调用并且如果在的实例上调用将this.constructorBaseBaseDerivedDerived

遗憾的是 typescript 没有识别this.constructor为正确的类型,这将是类型的构造函数new (...any[]) => this(参见https://github.com/microsoft/TypeScript/issues/3841)所以我只是将它转换为any. 也许有更好的方法来做到这一点,我不知道。

如果你必须更频繁地调用构造函数,你可以为它创建一个方法,这样你就可以更容易地编写它:

class Base {
  constructor(public name: string) {}
  newInstance(...args: any[]): this { return new (this.constructor as any)(...args); }
  funcA(): this { return this.newInstance('FUNC_A') }
  funcB(): this { return this.newInstance('FUNC_B') }
}

class Derived extends Base {
  constructor() { super('DERIVED') }
  funcA(): this { return super.funcA() }
  newFunc(): this { return this.newInstance() }
}

Here is the TS Playground I used for testing: https://www.typescriptlang.org/play?#code/MYGwhgzhAEBCkFNoG8BQ1rAPYDsIBcAnAV2Hy0IAoAHYgIxAEthocwBbBALmgMMZwBzAJQoAvulYIA7gEk8+MDmAJKAOg1hCgiDyUBPANoBdYT3wALRjGTRCCfMUI4p06JUvW12BSTIVoSECcfWF1TW0IYQBuaAkMADNiZQBBSjNoTxs7BycXLLUcGXkCJRVKAHIAMQBVADkAYQB9FIrReOgk5Vh08yts+0dnTP7C4oUy1Wr65tg2uNQJVFBIGAARBH4ANwQAE2gEAA98BBxdmHgIJDQMHz5ScipRWwhiak3KtYBRACVZADUvmt5h0usA0hksigckMXK93oQ1GCIQsMEVpFVksBeiNrNDBnlcRAxnIJspVO1FqhlrgIFgQAg1CAsIJKOjoBttnt0kisRCSZjlOkYjS8PTGczWezLqoAERgWXCXmpHlgnrCEVAA


推荐阅读