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

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,再通过映射类型构造出目标结构的类型定义:

  1. 给helper函数定义泛型参数T,约束T的每个属性都是Action<any>的子类型;
  2. 构造返回值类型:将T的每个键映射到can和do对象中,同时提取对应Action的参数类型作为方法的参数类型;
  3. 用类型断言给初始的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 19:06:00