无法将抽象构造函数类型分配给非抽象构造函数
Cannot assign an abstract constructor type to a non-abstract constructor
我如何用 typescript 解决这个问题?
ex: 当你有一个 class 寄存器,当你想创建一个所有 classes 注册的实例时?
export abstract class FooAbstract {
static registers: typeof FooAbstract[] = []; // a registers
static create<T extends FooAbstract>(constr: new ()=>T) {
return new constr();
}
}
export class A extends FooAbstract {
name:string;
}
FooAbstract.registers.push(A); // in my case i use decoration, but not matter !
// what should be the good way here ?
const test = FooAbstract.registers.map((cls)=> cls.create(cls))
Argument of type 'typeof FooAbstract' is not assignable to parameter of type 'new () => FooAbstract'.
Cannot assign an abstract constructor type to a non-abstract constructor type.ts(2345)
问题出在这一行 (constr: new () => T)
。
create
静态方法需要常规构造函数,而不是抽象构造函数,而您正试图将抽象构造函数传递给此方法。
请参阅此处 FooAbstract.registers.map((cls) => cls.create(cls))
。 cls.create
期望 new () => T
但您正在尝试使用 abstract new () => T
.
您可以将 abstract
flag/modifier 添加到 constr
参数。
export abstract class FooAbstract {
static registers: typeof FooAbstract[] = [];
static create<T extends FooAbstract>(constr: abstract new () => T) { // add `abstract` modifier
return new constr();
}
}
const test = FooAbstract.registers.map((cls) => cls.create(cls))
但是上面的方法,导致了一个新的问题。不允许创建抽象实例 class、new constr() // <--- error is here
.
不安全的方法,可能会导致运行时错误。查看@kaya3 评论
如果你想创建一些class的实例,这个class不应该是abstract
。
因此,我们需要创建一个空的匿名 class 来扩展我们的摘要 class:
export abstract class FooAbstract {
static registers: typeof FooAbstract[] = [];
static create = <T extends FooAbstract>(constr: new () => T) => new constr()
}
const test = FooAbstract.registers.map((cls) =>
cls.create(class extends cls { }) // <--- anonymous class is just a wrapper
)
还有另一种解决方案。您可以从 FooAbstract
class 定义中删除 abstract
关键字。
P.S.我不是OOP专家,所以我不能说我提供的答案是否符合OOP良好实践。我已经向您展示了如何修复 TS 错误
P.P.S 如果使用 abstract
class 甚至 OOP 方法对你来说并不重要,我可以推荐另一种方法,如果您将为您的问题提供更多详细信息。
注册表中的每个 class 都必须 new
-able 且不带任何参数。这意味着它不应该被允许包含抽象的 classes,或者其构造函数需要参数的 classes。您需要更改注册表的类型以指定它。我写了一个 NoArgsConstructor
辅助类型来简化这个。
type NoArgsConstructor<T> = new () => T
export abstract class FooAbstract {
static registers: NoArgsConstructor<FooAbstract>[] = [];
static create<T extends FooAbstract>(constr: NoArgsConstructor<T>): T {
return new constr();
}
}
export class A extends FooAbstract {
name:string = 'foo';
}
FooAbstract.registers.push(A);
const test = FooAbstract.registers.map(cls => FooAbstract.create(cls));
另请注意,对静态 create
方法的调用需要在 FooAbstract
上进行,而不是在 cls
.
上进行
与@captain-yossarian 的遮阳棚相关
我保留这种方法,哪里工作正常。
但是没有包装 class 哪里不是粉丝。
所以我 return 只是 new ()=>T
没有抽象标志(Type Guard!)
export abstract class FooAbstract{
static registers: typeof FooAbstract[] = [];
// adding abstract, and than return wihout abstract
static create<T extends FooAbstract>(constr: abstract new ()=>T) {
return new (constr as new ()=>T)();
}
}
export class A extends FooAbstract {
name:string;
}
FooAbstract.registers.push(A); // in my case i use decoration, but not matter !
// laters
const test = FooAbstract.registers.map((cls)=> cls.create(cls ))
我如何用 typescript 解决这个问题?
ex: 当你有一个 class 寄存器,当你想创建一个所有 classes 注册的实例时?
export abstract class FooAbstract {
static registers: typeof FooAbstract[] = []; // a registers
static create<T extends FooAbstract>(constr: new ()=>T) {
return new constr();
}
}
export class A extends FooAbstract {
name:string;
}
FooAbstract.registers.push(A); // in my case i use decoration, but not matter !
// what should be the good way here ?
const test = FooAbstract.registers.map((cls)=> cls.create(cls))
Argument of type 'typeof FooAbstract' is not assignable to parameter of type 'new () => FooAbstract'.
Cannot assign an abstract constructor type to a non-abstract constructor type.ts(2345)
问题出在这一行 (constr: new () => T)
。
create
静态方法需要常规构造函数,而不是抽象构造函数,而您正试图将抽象构造函数传递给此方法。
请参阅此处 FooAbstract.registers.map((cls) => cls.create(cls))
。 cls.create
期望 new () => T
但您正在尝试使用 abstract new () => T
.
您可以将 abstract
flag/modifier 添加到 constr
参数。
export abstract class FooAbstract {
static registers: typeof FooAbstract[] = [];
static create<T extends FooAbstract>(constr: abstract new () => T) { // add `abstract` modifier
return new constr();
}
}
const test = FooAbstract.registers.map((cls) => cls.create(cls))
但是上面的方法,导致了一个新的问题。不允许创建抽象实例 class、new constr() // <--- error is here
.
不安全的方法,可能会导致运行时错误。查看@kaya3 评论
如果你想创建一些class的实例,这个class不应该是abstract
。
因此,我们需要创建一个空的匿名 class 来扩展我们的摘要 class:
export abstract class FooAbstract {
static registers: typeof FooAbstract[] = [];
static create = <T extends FooAbstract>(constr: new () => T) => new constr()
}
const test = FooAbstract.registers.map((cls) =>
cls.create(class extends cls { }) // <--- anonymous class is just a wrapper
)
还有另一种解决方案。您可以从 FooAbstract
class 定义中删除 abstract
关键字。
P.S.我不是OOP专家,所以我不能说我提供的答案是否符合OOP良好实践。我已经向您展示了如何修复 TS 错误
P.P.S 如果使用 abstract
class 甚至 OOP 方法对你来说并不重要,我可以推荐另一种方法,如果您将为您的问题提供更多详细信息。
注册表中的每个 class 都必须 new
-able 且不带任何参数。这意味着它不应该被允许包含抽象的 classes,或者其构造函数需要参数的 classes。您需要更改注册表的类型以指定它。我写了一个 NoArgsConstructor
辅助类型来简化这个。
type NoArgsConstructor<T> = new () => T
export abstract class FooAbstract {
static registers: NoArgsConstructor<FooAbstract>[] = [];
static create<T extends FooAbstract>(constr: NoArgsConstructor<T>): T {
return new constr();
}
}
export class A extends FooAbstract {
name:string = 'foo';
}
FooAbstract.registers.push(A);
const test = FooAbstract.registers.map(cls => FooAbstract.create(cls));
另请注意,对静态 create
方法的调用需要在 FooAbstract
上进行,而不是在 cls
.
与@captain-yossarian 的遮阳棚相关 我保留这种方法,哪里工作正常。 但是没有包装 class 哪里不是粉丝。
所以我 return 只是 new ()=>T
没有抽象标志(Type Guard!)
export abstract class FooAbstract{
static registers: typeof FooAbstract[] = [];
// adding abstract, and than return wihout abstract
static create<T extends FooAbstract>(constr: abstract new ()=>T) {
return new (constr as new ()=>T)();
}
}
export class A extends FooAbstract {
name:string;
}
FooAbstract.registers.push(A); // in my case i use decoration, but not matter !
// laters
const test = FooAbstract.registers.map((cls)=> cls.create(cls ))