如果我有一个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
类成为非抽象类,并使所有应该应该为抽象抛出错误的方法。
答案 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
请注意,未知chickenAdapter
是ChickenAdapter
实例,因为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'.
也不错。
好的,希望能有所帮助;祝你好运!
答案 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更改为每个类都必须实现的接口,而不是使用抽象类。