TypeScript实现数组对象属性分组计数时索引类型报错问题
问题描述
以下是存在类型报错的TS实现:
export const groupBy = <T, K extends keyof T>(key: K, array: T[]) => array.reduce((objectsByKeyValue, obj) => { const value = obj[key]; objectsByKeyValue[value] = (objectsByKeyValue[value] || 0) + 1; return objectsByKeyValue; }, {});
相关需求与问题:
- 类型约束要求:仅允许传入数组内对象类型所包含的属性键作为入参
- 预期功能:传入对象数组后,按对象的指定属性对元素分组,统计各属性值对应的出现次数
- 当前报错:实现第4行抛出TS类型错误:
Type 'T[K]' cannot be used to index type '{}'.
错误原因
报错根源是reduce方法传入的初始值是无类型标注的空对象{},TS默认将其推断为无任何索引签名的空对象类型,既不支持用T[K]类型的键做索引访问,也未声明存储的属性值为数字类型,因此触发类型校验错误。
另外原类型定义缺少对分组属性取值的约束:JS仅支持string | number | symbol类型作为对象键,如果传入的属性值是对象、数组等类型,会被隐式转换为"[object Object]"这类字符串,导致统计结果异常,需要在类型层做拦截。
修复后实现
export const groupBy = <T, K extends keyof T>( key: T[K] extends PropertyKey ? K : never, array: T[] ): Record<T[K], number> => array.reduce((objectsByKeyValue, obj) => { const value = obj[key]; objectsByKeyValue[value] = (objectsByKeyValue[value] || 0) + 1; return objectsByKeyValue; }, {} as Record<T[K], number>);
修复点说明
- 新增入参类型校验:仅当指定属性的取值为合法对象键(
PropertyKey,即string | number | symbol)时,该属性名才允许作为入参传入,非法属性会在TS校验阶段直接报错,满足入参约束要求 - 给
reduce初始值添加类型断言,标记为Record<T[K], number>类型,明确该对象的键为所选属性的取值类型、值为统计用的数字,解决空对象无法被T[K]索引的类型错误 - 明确标注函数返回值类型,调用函数时可直接获得完整类型提示,无需额外做类型断言
内容的提问来源于stack exchange,提问作者NicoleZ
相关产品推荐
相关产品推荐

