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

TypeScript中myFunction返回类型异常,参数类型提取不符合预期

解决TypeScript中action函数的参数类型约束问题

你当前的代码无法正确约束action函数的参数,核心问题是遍历联合类型的type时,没有针对每个type提取对应的payload结构,导致所有action的参数都是所有payload类型的交集,无法区分不同action的参数要求。

以下是修正后的完整代码:

// 辅助类型:从payload联合类型中提取指定type对应的结构
type ExtractActionPayload<Payload extends { type: string }, T extends Payload['type']> = 
  Extract<Payload, { type: T }>

// 重新定义Base类型,更精准描述reducer结构
type Base = {
  [key: string]: <S>(state: S, payload: { type: string } & Record<string, any>) => S
}

const myFunction = <T extends Base>(arg: T) => {
  return {
    actions: {} as {
      [Domain in keyof T]: {
        [ActionType in Parameters<T[Domain]>[1]['type']]: 
          // 提取当前actionType对应的payload结构
          ExtractActionPayload<Parameters<T[Domain]>[1], ActionType> extends infer P
            ? // 移除type字段后,剩余属性为空则无需参数,否则传入对应属性
              Omit<P, 'type'> extends {} 
                ? () => void 
                : (payload: Omit<P, 'type'>) => void
            : never
      }
    }
  }
}

// 测试代码
const reducer1 = (state: boolean, payload: { type: 'foo', payload: number } | { type: 'batman' }) => state
const reducer2 = (state: string, payload: { type: 'bar' }) => state

const { actions } = myFunction({
  domain1: reducer1,
  domain2: reducer2
})

// 验证预期行为:
actions.domain1.batman() // ✅ 无需参数
actions.domain1.foo({ payload: 123 }) // ✅ 需要传入number类型的payload
// actions.domain1.foo() ❌ 报错,缺少必要参数
actions.domain2.bar() // ✅ 无需参数

关键逻辑说明

  1. ExtractActionPayload辅助类型:利用TypeScript内置的Extract工具类型,从payload联合类型中筛选出与当前actionType匹配的结构,确保每个action对应正确的payload定义。
  2. 条件类型处理参数:通过Omit<P, 'type'>移除固定的type字段后,判断剩余属性是否为空:
    • 为空则说明该action不需要额外参数,函数定义为() => void;
    • 不为空则要求传入对应的payload属性,函数定义为接收该结构的参数。
  3. 修正Base类型:将reducer定义为泛型函数,帮助TypeScript更精准地推断state和payload的类型,避免any导致的类型信息丢失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 03:07:15