【问题标题】:Cannot assign an abstract constructor type to a non-abstract constructor无法将抽象构造函数类型分配给非抽象构造函数
【发布时间】:2021-11-24 03:52:51
【问题描述】:

我如何用 typescript 解决这个问题?

ex:当你有一个类注册时,你想创建一个所有注册类的实例?


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)

【问题讨论】:

  • 为什么不创建生成器函数工厂而不是基于类型的工厂?努力是完全一样的,但没有痛苦。
  • 我是 typescript 的新手,你有这方面的 doc 参考吗?
  • 请不要编辑问题以包含您自己的答案 - 这会使读者感到困惑,并绕过允许其他用户标记哪些答案最有帮助的投票机制。如果您对自己的问题有答案,则应将其作为答案发布。
  • ho oki 对,有意义

标签: typescript oop abstract-class


【解决方案1】:

问题出在(constr: new () =&gt; T) 这一行。 create 静态方法需要一个常规构造函数,而不是一个抽象构造函数,而您正试图将一个抽象构造函数传递给这个方法。 见这里FooAbstract.registers.map((cls) =&gt; cls.create(cls))cls.create 期望 new () =&gt; T 但您正在尝试使用 abstract new () =&gt; T

您可以将abstract 标志/修饰符添加到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))

但上述方法会导致一个新问题。不允许创建抽象类的实例new constr() // &lt;--- error is here

不安全的方法,可能会导致运行时出错。查看@kaya3 评论

如果你想创建某个类的实例,这个类不应该是abstract

因此,我们需要创建一个空的匿名类来扩展我们的抽象类:

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 类定义中删除 abstract 关键字。

P.S.我不是 OOP 专家,所以我不能说我提供的答案是否符合 OOP 的良好实践。我已经向您展示了如何修复 TS 错误

P.P.S如果使用 abstract 类甚至 OOP 方法对您来说并不重要,如果您愿意为您的问题提供更多详细信息,我可以推荐另一种方法。

【讨论】:

  • 这不安全。你不能只写class extends cls {} 来创建任意抽象类的具体子类。如果cls 是抽象的,那么您应该期望它在其接口中缺少某些方法的实现。在这种情况下,您的匿名子类也缺少这些方法,因此实例化它是不安全的。真正的解决方案是首先要求注册表中的类不是抽象的。
  • 哼,你给我个主意,怎么样? return new (constr as new ()=&gt;T)(); 看起来不错,但在 ts 中安全吗?这是一个很好的apraoche 吗? ` static create(constr: abstract new ()=>T) { return new (constr as new ()=>T)(); } `
  • oki 谢谢大家的帮助,我用我保留的方法编辑我的答案。 :)
  • @kaya3 看到这个 tsplay.dev/Wkj7lm 。是什么让你觉得anonymous subclass is also missing those methods
  • 是的,这还不错,但是我对此并不放心。 cls.create (class extends cls {}),这样包装,性能我也不确定!
【解决方案2】:

注册表中的每个类都必须是 new-able 且不带任何参数。这意味着它不应该被允许包含抽象类,或者其构造函数需要参数的类。您需要更改注册表的类型来指定它。我写了一个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

【讨论】:

  • oki 谢谢大家的帮助,我用我保留的方法编辑我的答案。 :)
【解决方案3】:

与@captain-yossarian 的遮阳篷有关 我保留了这种方法在哪里可以正常工作。 但是没有不喜欢的包装类。

所以我只返回一个 new ()=&gt;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 ))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 2015-03-20
    • 2011-03-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多