You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为SchedulerService的defined对象传递泛型以获取带类型调度列表?

问题描述

我想要维护一个包含类型信息的已定义调度列表,计划用defined对象存储,但没法给这个对象传递泛型类型,代码报错无法传递所需类型,以下是我的代码:

@Injectable()
export class SchedulerService {
  private readonly scheduler: Agenda;
  private readonly defined: Record<string, Processor<T>> = {};
  // or   private readonly defined: Map<string, Processor<T>> = new Map();

  constructor(private readonly config: ConfigService) {
    this.scheduler = new Agenda({
      db: { address: this.config.get('MONGO_URI'), collection: 'AgendaJobs' },
    });

    this.scheduler.start();
  }

  getDefined() {
    return this.defined;
  }

  getDefinedNames() {
    return Object.keys(this.defined);
  }

  async defineSchedule<T>(name: string, callback: Processor<T>, context: any) {
    this.defined[name] = callback.bind(context);

    this.scheduler.define<T>(name, callback.bind(context));
  }

  async createSchedule<T>(time: string | Date, name: string, data: T) {
    const job = await this.scheduler.schedule<T>(time, name, {
      time,
      ...data,
    });

    return String(job.toJSON()._id);
  }

  async cancelJobs(filter: FilterQuery<Document>) {
    return await this.scheduler.cancel({ ...filter, nextRunAt: { $ne: null } });
  }
}
解决方案

核心问题是defined成员声明时使用了未定义的泛型参数T,且不同调度任务可能对应不同的T类型,单个泛型参数无法覆盖所有场景,以下是几种可行的解决方式:

1. 为服务类添加泛型映射(类型安全最优)

如果能提前确定所有调度任务的名称和对应数据类型,可以给SchedulerService定义泛型接口,约束每个调度名称对应的Processor类型:

// 示例:定义所有任务的类型映射,可根据实际业务调整
type JobTypeMap = {
  sendEmail: Processor<{ to: string; content: string }>,
  generateReport: Processor<{ reportId: number; scope: string }>
};

@Injectable()
export class SchedulerService<T extends JobTypeMap = JobTypeMap> {
  private readonly scheduler: Agenda;
  private readonly defined: T = {} as T;

  constructor(private readonly config: ConfigService) {
    this.scheduler = new Agenda({
      db: { address: this.config.get('MONGO_URI'), collection: 'AgendaJobs' },
    });
    this.scheduler.start();
  }

  getDefined() {
    return this.defined;
  }

  getDefinedNames() {
    return Object.keys(this.defined) as Array<keyof T>;
  }

  async defineSchedule<K extends keyof T>(name: K, callback: T[K], context: any) {
    this.defined[name] = callback.bind(context) as T[K];
    this.scheduler.define(name, callback.bind(context));
  }

  async createSchedule<K extends keyof T>(
    time: string | Date, 
    name: K, 
    data: Parameters<T[K]>[0]['attrs']
  ) {
    const job = await this.scheduler.schedule(time, name, {
      time,
      ...data,
    });
    return String(job.toJSON()._id);
  }

  async cancelJobs(filter: FilterQuery<Document>) {
    return await this.scheduler.cancel({ ...filter, nextRunAt: { $ne: null } });
  }
}

这种方式让TypeScript严格校验每个调度名称对应的处理器和数据类型,完全保留类型安全。

2. 使用unknown兼容多类型(快速解决)

如果不需要严格的类型校验,仅需消除语法错误,可以将defined的类型声明改为:

private readonly defined: Record<string, Processor<unknown>> = {};

也可以用any替代unknown,但会完全丢失类型检查,不推荐在需要类型约束的场景使用。

3. 泛型函数配合类型断言(灵活但需手动维护)

如果不想给整个类添加泛型,可以在defineSchedule中通过类型断言维护类型关联,同时新增方法获取指定类型的处理器:

private readonly defined: Record<string, Processor<unknown>> = {};

async defineSchedule<T>(name: string, callback: Processor<T>, context: any) {
  this.defined[name] = callback.bind(context) as Processor<unknown>;
  this.scheduler.define<T>(name, callback.bind(context));
}

// 获取指定类型的处理器,需手动确保名称对应正确类型
getProcessor<T>(name: string): Processor<T> | undefined {
  return this.defined[name] as Processor<T>;
}

这种方式灵活性高,但需要开发者手动维护类型与名称的对应关系,存在类型不匹配的风险。

内容的提问来源于stack exchange,提问作者Roman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 19:05:23