typescript 键入动态创建函数

yeotifhr  于 2023-03-19  发布在  TypeScript
关注(0)|答案(1)|浏览(142)

给定以下代码:

class Service {
  actions: Record<string, Function>;
}

class ServiceRunner<S extends typeof Service> {
  #service: Service
  constructor(ServiceClass: S) {
    this.#service = new ServiceClass();
    for (const [actionName, actionFn] of Object.entries(this.#service.actions)) {
      this[actionName] = (...args: any[]) => actionFn(...args);
    }
  }
}

class Foo extends Service {
  constructor() {
    super();
    console.log("constructor of Foo")
  }
  actions: {
    action1: () => {};
    action2: (id: string) => {};
  };
  anotherMethodThatIsNotAnAction() {}
}

const runner = new ServiceRunner(Foo)
runner.action1(); // My goal would be to have completion and typing on "action1" here
runner.action2("ok") // the same

正如你所看到的,我在ServiceRunner中动态地构建了一些函数,这些函数直接Map到给定的内部Service函数,它工作正常(javascript代码运行正确),但是我没有任何线索来输入这些函数,以使它们等价于Foo服务(或Service的任何给定后代)中定义的函数。
我甚至不知道这是否可能。

q5iwbnjs

q5iwbnjs1#

一个类可以有一个索引签名,但是它不能有一个没有声明的静态类型成员。
如果ServiceRunner有其他不是来自服务类的成员,我会提醒您不要使用这种方法,因为可能会发生名称冲突。
但是,如果您创建一个单独的构造函数签名来为返回的示例创建正确的类型,您可以在TS中实现这一点:

type ServiceRunner<S extends Service> = ServiceRunnerBase & S['actions']
const ServiceRunner = ServiceRunnerBase as new <S extends Service>(ServiceClass: new () => S) => ServiceRunner<S>

Playground链接
如果在ServiceRunner中没有任何额外的成员,您还可以将其简化为:

type ServiceRunner<S extends Service> = S['actions']
const ServiceRunner = ServiceRunnerBase as new <S extends Service>(ServiceClass: new () => S) => ServiceRunner<S>

Playground链接

相关问题