无法将抽象构造函数类型分配给非抽象构造函数

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
)

Playground

还有另一种解决方案。您可以从 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.

上进行

Playground Link

与@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 ))