TypeScript严格模式下reduce分组函数报错:Type 'undefined'不可用作索引类型
解决TypeScript严格模式下groupBy函数的"Type 'undefined' cannot be used as an index type"错误
问题场景
你需要实现一个函数,将对象数组按指定key的值分组,例如输入:
const input = [{name: 'name-1', foo: 'bar'}, {name: 'name-2', foo: 'bar'}];
期望输出:
{ 'bar': [{name: 'name-1', foo: 'bar'}, {name: 'name-2', foo: 'bar'}] }
但使用reduce实现时,TypeScript严格模式下触发错误:Type 'undefined' cannot be used as an index type。
错误代码示例
通常出错的代码类似这样:
function groupBy<T>(array: T[], key: keyof T) { return array.reduce((acc, item) => { const groupKey = item[key]; // 此处触发索引类型错误 acc[groupKey] = acc[groupKey] || []; acc[groupKey].push(item); return acc; }, {} as Record<string, T[]>); }
错误原因
TypeScript严格模式下,keyof T对应的属性值可能包含undefined(比如T中该属性是可选类型,或属性值类型允许undefined),而对象的索引类型只能是string、number或symbol,因此直接用可能为undefined的groupKey作为索引会触发错误。
解决方案
方案1:约束分组键为合法索引类型
通过泛型约束,确保指定key对应的属性值必须是string/number/symbol类型,从根源避免undefined作为索引:
function groupBy<T, K extends keyof T>( array: T[], key: K ): Record<T[K] extends string | number | symbol ? T[K] : never, T[]> { return array.reduce((acc, item) => { const groupKey = item[key] as T[K] & (string | number | symbol); acc[groupKey] = acc[groupKey] || []; acc[groupKey].push(item); return acc; }, {} as Record<T[K] extends string | number | symbol ? T[K] : never, T[]>); }
方案2:处理undefined分组键
如果业务需要保留属性值为undefined的项,可将undefined转换为合法的索引值(比如字符串'undefined'):
function groupBy<T>(array: T[], key: keyof T) { return array.reduce((acc, item) => { const groupKey = item[key]; // 将undefined转为字符串键,确保索引合法 const safeKey = groupKey === undefined ? 'undefined' : (groupKey as string | number | symbol); acc[safeKey] = acc[safeKey] || []; acc[safeKey].push(item); return acc; }, {} as Record<string | number | symbol, T[]>); }
方案3:过滤属性值为undefined的项
如果业务不需要将属性值为undefined的项纳入分组,直接过滤这些元素即可:
function groupBy<T>(array: T[], key: keyof T) { return array.reduce((acc, item) => { const groupKey = item[key]; // 仅处理属性值不为undefined的项 if (groupKey !== undefined) { const safeKey = groupKey as string | number | symbol; acc[safeKey] = acc[safeKey] || []; acc[safeKey].push(item); } return acc; }, {} as Record<string | number | symbol, T[]>); }
测试示例
使用任意一种方案测试:
const input = [{name: 'name-1', foo: 'bar'}, {name: 'name-2', foo: 'bar'}]; const result = groupBy(input, 'foo'); console.log(result); // 输出:{ bar: [ { name: 'name-1', foo: 'bar' }, { name: 'name-2', foo: 'bar' } ] }
内容的提问来源于stack exchange,提问作者user3142695
相关产品推荐
相关产品推荐

