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
相关产品推荐
相关产品推荐

