如何将抽象类转换为非抽象类?

时间:2019-09-04 09:01:32

标签: typescript abstract-class

如果我有一个abstract class Adapter {},我想创建一个函数,该函数接受扩展该类的非抽象构造函数并返回该类的新实例。

例如

export abstract class Adapter {}

export function change(adapterConstructor: typeof Adapter) {
  return new adapterConstructor();
}

不幸的是,此示例无法正常工作,因为打字稿编译器正确地抱怨无法实例化抽象类。

是否可以将adapterConstructor参数限制为仅typeof Adapter的非抽象实现?

即像

export abstract class Adapter {}

export function change(adapterConstructor: NonAbstract<typeof Adapter>) {
  return new adapterConstructor();
}

目前,我的解决方案是简单地使Adapter类成为非抽象类,并使所有应该应该为抽象抛出错误的方法。

2 个答案:

答案 0 :(得分:2)

是的,您可以要求change()方法将typeof Adapter(未知的是可更新的)与构造函数签名相交。

首先,由于empty classes behave strangely in TypeScript,我将给Adapter一些结构,我们不希望这使我们绊倒。另外,我想向Adapter添加一个静态方法,以便使自己确信change()可以调用它:

export abstract class Adapter {
  prop = "adapterProp";
  static staticMethod() {
    console.log("HI THERE");
  }
}

这是change()签名:

export function change(
  adapterConstructor: typeof Adapter & (new () => Adapter)
) {
  adapterConstructor.staticMethod();
  return new adapterConstructor();
}

您可以看到adapterConstructor既可更新,又具有Adapter的静态方法。请注意,构造函数签名为new () => Adapter,这意味着它不需要任何参数并生成Adapter

让我们确保它的行为符合您的要求:

const cannotConstructAbstract = new Adapter(); // error!
change(Adapter); // error! cannot assign abstract to non-abstract

您不能new Adapter类,也不能根据需要调用change(Adapter)

现在让我们创建一个具体的子类:

class ChickenAdapter extends Adapter {
  cluck() {
    console.log("B'KAW!");
  }
}

const canConstruct = new ChickenAdapter(); // okay
const chickenAdapter = change(ChickenAdapter); // okay

可以 new ChickenAdapter类,因此您可以调用change(ChickenAdapter),它返回一个Adapter

chickenAdapter.prop // okay
chickenAdapter.cluck(); // error! it only returns an Adapter, not a ChickenAdapter

请注意,未知chickenAdapterChickenAdapter实例,因为change()返回了Adapter。如果您希望change()对此进行跟踪,则可以在返回类型中使其通用:

export function change<T extends Adapter>(
  adapterConstructor: typeof Adapter & (new () => T)
) {
  adapterConstructor.staticMethod();
  return new adapterConstructor();
}

然后chickenAdapter.cluck()起作用:

chickenAdapter.cluck(); // okay

让我们继续介绍一些极端情况:

class CowAdapter extends Adapter {
  constructor(public color: string) {
    super();
  }

  moo() {
    console.log("MRRROOOORM!");
  }
}

const howNow = new CowAdapter("brown");
const butNeedsAnArgument = new CowAdapter(); // error! expected 1 argument
change(CowAdapter); // error!  CowAdapter needs arguments

在这里,change()不接受CowAdapter,因为该类需要一个构造函数参数,change()的主体不会传递该参数。这可能是正确的选择。

此外,还要确保您不能传递静态方面看起来不错但不会产生Adapter的内容:

class NotAnAdapter {
  static staticMethod() {}
  notAnAdapter = true;
}

change(NotAnAdapter); // error!
//Property 'prop' is missing in type 'NotAnAdapter' but required in type 'Adapter'.

也不错。


好的,希望能有所帮助;祝你好运!

Link to code

答案 1 :(得分:1)

您可以动态化,但是冒着允许Adapter自身传递的风险,这有点违反了抽象类的目的...

export abstract class Adapter {}

export function change(adapterConstructor: typeof Adapter): Adapter {
    const constr: any = adapterConstructor;
    return new constr() as Adapter;
}

否则,您需要引入一个具体的基类来代替Adapter ...或将Adapter更改为每个类都必须实现的接口,而不是使用抽象类。