如何为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
相关产品推荐
相关产品推荐

