打字稿中抽象类中的静态工厂

时间:2021-03-20 16:47:04

标签: typescript

考虑以下事项:

export type JSONable = Record<string, string | number>

export type Processable = {
  process(args?: JSONable): Promise<void>
}

abstract class AbstractTaskProcessor<T extends JSONable> implements Processable {

  abstract process(args?: T): Promise<void>

}

这是我工作系统的一部分。但有时为了测试我想马上测试流程方法,所以我想添加一个像这样的静态辅助方法

abstract class AbstractTaskProcessor<T extends JSONable> implements Processable {

  abstract process(args?: T): Promise<void>

  static runNow(args?: T): Promise<void> {
    const instance = new this()
    return instance.process(args)
  }
}

但我得到一个 TS2511: Cannot create an instance of an abstract class.

我希望 DSL 类似于......

MyTaskProcessor.runNow({accountId: 123123, accountName: 'Lorum' })

有没有人对此有好的模式?


我使用的是 TS 4.2

2 个答案:

答案 0 :(得分:4)

如果您只是对@ccarton 显示的方式进行类型转换,您就可以使您的示例工作。但如果它仅用于测试并且您想保留 T extends JSONable 的类型信息,我建议为它编写一个函数,例如 this:

class MyTaskProcessor extends  AbstractTaskProcessor<{test: number, test2: string}> {
     process(args?: {test: number, test2: string}): Promise<void> {
         return Promise.resolve();
     }
}

type Processable<T> = {
  process(args?: T): Promise<void>
}

export function runNow<T extends JSONable>(processorClass: new () => Processable<T>, args: T) {
  const instance = new processorClass();
  return instance.process(args)
}

runNow(MyTaskProcessor, {test: 5, test2: "hello"})
runNow(MyTaskProcessor, {test: 5, test2: 3}) // Type 'number' is not assignable to type 'string'
MyTaskProcessor.runNow({test: 5, test2: 3}) // no typesafety, no warning

使用@Linda Paiste 的精炼版进行更新

答案 1 :(得分:2)

从 Typescript 4.2 开始,现在支持抽象构造函数签名。请参阅:https://devblogs.microsoft.com/typescript/announcing-typescript-4-2/#abstract-construct-signatures

如果由于某种原因您无法升级,旧的解决方案需要常规构造函数签名和类型断言:

type JSONable = Record<string, string | number>

type Processable = {
  process(args?: JSONable): Promise<void>
}

type ProcessableConstructor = { new (): Processable }  // Constructor signature

abstract class AbstractTaskProcessor<T extends JSONable> implements Processable {

  abstract process(args?: T): Promise<void>

  static runNow<T extends JSONable> (args?: T): Promise<void> {
    const instance = new (this as any as ProcessableConstructor)()   // Type assertion
    return instance.process(args)
  }
}

class MyTaskProcessor extends AbstractTaskProcessor<JSONable> {
  async process (args: JSONable) { return }
}

MyTaskProcessor.runNow({accountId: 123123, accountName: 'Lorum' })