TypeScript如何从映射父类型映射元素并保留参数类型?
如何在TypeScript中重映射参数化类型以保留子类型信息
我有一组参数化的Action<P>类型实例,想要把它们重映射成一个更友好的结构——将所有实例的can方法和do方法分别归类到can和do两个对象下。但直接使用映射类型时,似乎无法引用原类型的子元素或其参数类型,请问该如何为这个重映射过程添加正确的类型定义,保留原类型中的类型信息?
示例代码:
class Action<P> { can(value: P): boolean { // stuff return true; } do(value: P): boolean { // stuff return true; } } const actionSet = { increment: new Action<string>(), foo: new Action<Date>(), baz: new Action<boolean>(), } function helper(actions: {[k: string]: Action<any>}) { const set = { can: {} as any, // 需要替换为正确类型 do: {} as any, // 需要替换为正确类型 } for (const key in actions) { set.can[key] = actions[key].can set.do[key] = actions[key].do } return set; } const friendlyActions = helper(actionSet);
解决方案:使用泛型+条件类型提取子类型信息
我们可以通过泛型约束和**条件类型推断(infer)**来提取每个Action<P>中的参数类型P,再通过映射类型构造出目标结构的类型定义:
- 给
helper函数定义泛型参数T,约束T的每个属性都是Action<any>的子类型; - 构造返回值类型:将
T的每个键映射到can和do对象中,同时提取对应Action的参数类型作为方法的参数类型; - 用类型断言给初始的
set对象指定正确类型,避免any的使用。
修改后的完整代码:
class Action<P> { can(value: P): boolean { // stuff return true; } do(value: P): boolean { // stuff return true; } } const actionSet = { increment: new Action<string>(), foo: new Action<Date>(), baz: new Action<boolean>(), } // 定义目标结构的类型别名 type ActionGroup<T> = { can: { [K in keyof T]: T[K] extends Action<infer P> ? (value: P) => boolean : never }; do: { [K in keyof T]: T[K] extends Action<infer P> ? (value: P) => boolean : never }; }; function helper<T extends Record<string, Action<any>>>(actions: T): ActionGroup<T> { const set = { can: {} as ActionGroup<T>['can'], do: {} as ActionGroup<T>['do'], }; for (const key in actions) { set.can[key] = actions[key].can; set.do[key] = actions[key].do; } return set; } const friendlyActions = helper(actionSet); // 现在可获得准确类型提示: // friendlyActions.can.increment 仅接收 string 类型参数 // friendlyActions.can.foo 仅接收 Date 类型参数 // friendlyActions.can.baz 仅接收 boolean 类型参数 // do 方法同理
关键逻辑说明
- 泛型约束:
T extends Record<string, Action<any>>确保传入的actions对象的每个属性都是Action实例; - 条件类型推断:
T[K] extends Action<infer P> ? (value: P) => boolean : never通过infer关键字自动提取Action的参数类型P,并将其作为对应方法的参数类型; - 映射类型:
[K in keyof T]遍历原对象的所有键,将其映射到新结构的can和do对象中,保证键名与原对象完全一致。
通过这种方式,friendlyActions会拥有完整的类型信息,调用方法时能获得准确的参数校验和提示,彻底替代any的模糊类型。
内容的提问来源于stack exchange,提问作者Tezra
相关产品推荐
相关产品推荐

