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

TypeScript编写groupBy泛型函数时的类型推断异常问题

TypeScript泛型groupBy函数类型报错问题解决

我尝试在TypeScript中编写一个groupBy泛型函数,希望将分组字段限制为对象中值类型为string、number或boolean的键。采用类型约束方案限制可使用的键后,这部分逻辑正常工作,但后续代码出现类型错误:

type KeysMatchingType<T, V> = {[K in keyof T]-?: T[K] extends V ? K : never}[keyof T];
type GroupByAllowedType = string | number | boolean 

function groupBy<T extends object>(arr: T[], prop: KeysMatchingType<T,GroupByAllowedType>): Map<GroupByAllowedType, T[]> {
  
  const map: Map<GroupByAllowedType, T[]> = new Map()
  return arr.reduce((acc, current) => {
    const key = current[prop]
    const val = acc.get(key) // 此处报错
    
    if(val){
      val.push(current)
    } else {
      acc.set(key, [current]) 
    }
    return acc
  }, map)
}

报错信息:

Type 'T[string]' is not assignable to type 'GroupByAllowedType'

尽管参数prop已被约束为能使T[prop]返回boolean、string或number的键,但TypeScript仍抛出上述错误,且我不想使用as进行类型断言。


问题原因

这是TypeScript的类型推断限制:虽然KeysMatchingType已经约束了prop是符合条件的键,但对于泛型T,TypeScript无法自动将current[prop]的类型缩小到GroupByAllowedType,而是会宽泛地推断为T[keyof T],导致类型不匹配。

解决方法

通过新增键类型泛型参数,让TypeScript明确关联prop对应的键类型和其值类型,就能解决这个问题:

type KeysMatchingType<T, V> = { [K in keyof T]-?: T[K] extends V ? K : never }[keyof T];
type GroupByAllowedType = string | number | boolean;

// 新增泛型参数K,约束为符合条件的键类型
function groupBy<T extends object, K extends KeysMatchingType<T, GroupByAllowedType>>(
  arr: T[],
  prop: K
): Map<T[K], T[]> {
  const map = new Map<T[K], T[]>();
  return arr.reduce((acc, current) => {
    const key = current[prop];
    const val = acc.get(key);
    
    if (val) {
      val.push(current);
    } else {
      acc.set(key, [current]);
    }
    return acc;
  }, map);
}

这种写法的优势:

  • TypeScript能准确推断key的类型为T[K],而T[K]必然属于GroupByAllowedType,无需类型断言
  • 返回的Map键类型是具体的子类型(比如某场景下是string,另一场景下是number),类型精度更高

如果希望返回的Map键类型统一为GroupByAllowedType,可以调整返回类型,同样无需断言:

type KeysMatchingType<T, V> = { [K in keyof T]-?: T[K] extends V ? K : never }[keyof T];
type GroupByAllowedType = string | number | boolean;

function groupBy<T extends object, K extends KeysMatchingType<T, GroupByAllowedType>>(
  arr: T[],
  prop: K
): Map<GroupByAllowedType, T[]> {
  const map = new Map<GroupByAllowedType, T[]>();
  return arr.reduce((acc, current) => {
    const key = current[prop];
    const val = acc.get(key);
    
    if (val) {
      val.push(current);
    } else {
      acc.set(key, [current]);
    }
    return acc;
  }, map);
}

总结

最初的写法问题在于泛型推断的局限性,仅通过KeysMatchingType约束prop无法让TypeScript反向推导值类型。新增明确的键类型泛型参数后,TypeScript能建立起键与值的类型关联,既解决了类型错误,又保持了类型安全。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 06:01:17