TypeScript中如何实现多级泛型以构建类型安全的ActivityExecutor?
解决方案:提取活动的输入输出类型,实现类型安全的执行器
要让ActivityExecutor保留活动的类型信息,核心是从传入的BaseActivity子类中提取对应的ActivityInput和ActivityOutput类型,用TypeScript的条件类型+泛型约束就能解决这个问题。
1. 先写类型提取工具
创建两个工具类型,专门用来从任意活动类里扒出它的输入、输出类型:
// 提取活动类的输入类型 type ExtractActivityInput<A extends BaseActivity<any, any>> = A extends BaseActivity<infer I, any> ? I : never; // 提取活动类的输出类型 type ExtractActivityOutput<A extends BaseActivity<any, any>> = A extends BaseActivity<any, infer O> ? O : never;
2. 实现类型安全的ActivityExecutor
调整ActivityExecutor的泛型定义,用上面的工具类型约束输入输出,同时优化实例化逻辑(避免每次执行都新建活动实例):
export class ActivityExecutor<A extends BaseActivity<any, any>> { // 传入活动类的构造函数,提前持有引用 constructor(private readonly activityClass: new () => A) {} execute(input: ExtractActivityInput<A>): ExtractActivityOutput<A> { // 这里加你的后台操作:日志、监控、异常捕获等 console.log("执行前的前置操作..."); const activity = new this.activityClass(); const result = activity.execute(input); console.log("执行后的后置操作..."); return result; } }
3. 实际使用(带完整类型提示)
现在用起来就能得到你想要的类型提示了:
const addNumbersExecutor = new ActivityExecutor(AddNumbers); // 输入会自动提示需要AddNumbersInput结构,输出自动推导为number const result = addNumbersExecutor.execute({ num1: 3, num2: 3 }); // result的类型是number,完全符合预期
补充:如果活动需要构造参数
要是你的活动类初始化需要传参数,直接传实例给执行器更合适:
export class ActivityExecutor<A extends BaseActivity<any, any>> { constructor(private readonly activity: A) {} execute(input: ExtractActivityInput<A>): ExtractActivityOutput<A> { // 后台逻辑照旧 return this.activity.execute(input); } } // 使用示例: const addNumbersActivity = new AddNumbers(); // 这里传入构造参数 const executor = new ActivityExecutor(addNumbersActivity); const result = executor.execute({ num1: 3, num2: 3 });
为啥原来的写法行不通?
你之前的泛型定义A extends BaseActivity<ActivityInput, ActivityOutput>里,ActivityInput和ActivityOutput是未声明的“悬空”类型参数,TypeScript没法把它们和A的实际类型参数关联起来。用infer关键字从A的继承关系里提取类型,就能让TypeScript自动推导对应的输入输出类型,实现类型安全和代码提示。
内容的提问来源于stack exchange,提问作者Moon
相关产品推荐
相关产品推荐

