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

TypeScript泛型函数将泛型接口类型折叠为unknown问题排查

问题:API结果合并函数的TypeScript类型推导异常

我写了一个合并多个API请求结果的函数,规则是所有请求无错误时合并data,只要有一个错误就返回该错误:

mergeAPIResults({ data: 123 }, { data: 'test' }) // 输出: { data: [ 123, 'test' ] }
mergeAPIResults({ data: 123 }, { error: 'failed' }) // 输出: { error: 'failed' }

实际用的是React Query的useQuery(),这里简化成了最简示例。问题是:一旦扩展原接口或使用鸭子类型的兼容接口,TypeScript会把推导后的类型折叠成unknown。

正常工作的代码

以下代码的类型推导完全符合预期:

interface APIResult<DataType> {
  data?: DataType
  error?: any
}

// 映射类型:将[ number, string, ... ]转为[ APIResult<number>, APIResult<string>, ... ]
type WrapWithAPIResults<T extends any[]> = {
  [K in keyof T]: APIResult<T[K]>
}

function mergeAPIResults<T extends any[]>(...apiResults: WrapWithAPIResults<T>): APIResult<T> {
  const error = apiResults.find(r => r.error)?.error

  if (error) {
    return { error }
  } else {
    return { data: apiResults.map(r => r.data) as T }
  }
}

const mergeTest = mergeAPIResults({ data: 123 }, { data: 'test' })
// ✅ 推导类型: APIResult<[number, string]>

const mergeTest2 = mergeAPIResults(mergeTest, { data: 456 })
// ✅ 推导类型: APIResult<[[number, string], number]>

出现类型推导问题的场景

当使用扩展原接口的类型时,TypeScript无法正确推导data的类型,会变成unknown:

interface ExtendedAPIResult<DataType> extends APIResult<DataType> {
  extraData: string
}

const extApiResult: ExtendedAPIResult<string> = { data: 'test', extraData: 'blah' }

const mergeTestError = mergeAPIResults({ data: 456 }, extApiResult)
// ❌ 推导类型: APIResult<[number, unknown]>
// 期望类型: APIResult<[number, string]>

哪怕移除extraData、创建和原接口完全一致的APIResult2,问题依然存在。但用React Query的UseQueryResult却能正常推导:

const mergeTestRQ = mergeAPIResults({ data: 456 }, useQuery({ queryFn: () => true }))
// ✅ 推导类型: APIResult<[number, boolean]>

问题原因&解决方案

原因

你的泛型约束是WrapWithAPIResults<T>,也就是让TypeScript从传入的APIResult<T[K]>实例反向推导T。但当传入的是APIResult的子类型(比如ExtendedAPIResult<string>)时,TypeScript的类型推导会选择最宽泛的匹配:ExtendedAPIResult<string>既符合APIResult<string>,也符合APIResult<unknown>(因为string兼容unknown)。此时TypeScript会默认选unknown,导致最终推导结果异常。

而React Query的UseQueryResult能正常工作,是因为它的泛型参数是显式绑定的,useQuery返回的是明确的UseQueryResult<boolean, Error>类型,TypeScript能直接提取到data的类型,不会触发宽泛匹配的问题。

解决方案

修改函数的泛型定义,直接从每个输入参数的类型中提取data的类型,而不是依赖反向推导:

interface APIResult<DataType> {
  data?: DataType
  error?: any
}

function mergeAPIResults<Results extends APIResult<any>[]>(
  ...apiResults: Results
): APIResult<{ [K in keyof Results]: Results[K]['data'] }> {
  const error = apiResults.find(r => r.error)?.error

  if (error) {
    return { error } as APIResult<never>
  } else {
    return { data: apiResults.map(r => r.data) as any }
  }
}

// 测试扩展接口的场景
interface ExtendedAPIResult<DataType> extends APIResult<DataType> {
  extraData: string
}
const extApiResult: ExtendedAPIResult<string> = { data: 'test', extraData: 'blah' }
const mergeTestFixed = mergeAPIResults({ data: 456 }, extApiResult)
// ✅ 推导类型: APIResult<[number, string]>

这种写法直接遍历输入参数数组的每个元素,提取其data字段的类型,不管输入是APIResult还是它的子类型,都能正确推导。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 01:37:22