升级TypeScript后toArrayByObjectKey函数类型兼容问题求助
升级TypeScript后
toArrayByObjectKey函数的类型错误解决方法 问题场景
升级TypeScript版本后,原有的toArrayByObjectKey函数出现编译错误,尝试官方提示的泛型约束方案后,测试代码又因Student类型缺少索引签名报错。相关代码及错误信息如下:
原函数代码
/** * For each instance of type `E` in the array argument * map the property values into arrays keyed by the name of the * property in the instance of type `E`. * @param entities The array of entities * @return A single object with all the property values on the entities mapped into arrays. * * @example students:Student[] = [ {id:1, name:'1', sex:'X', standard: 1, propName: 11, anotherSillyPropName:111,}, {id:2, name:'2', sex:'Y', standard: 2, propName: 22, anotherSillyPropName:222,}, {id:3, name:'3', sex:'Z', standard: 3, propName: 33, anotherSillyPropName:333,}, ] console.log(toArrayByObjectKey(students)) */ export function toArrayByObjectKey<E>(entities:E[]) { let res:any = {}; for (let obj of entities) for (let [k, v] of Object.entries(obj)) res[k] = (res[k] || []).concat(v) return res; };
升级后编译错误
No overload matches this call. Overload 1 of 2, '(o: { [s: string]: unknown; } | ArrayLike<unknown>): [string, unknown][]', gave the following error. Argument of type 'E' is not assignable to parameter of type '{ [s: string]: unknown; } | ArrayLike<unknown>'. Type 'E' is not assignable to type 'ArrayLike<unknown>'. Overload 2 of 2, '(o: {}): [string, any][]', gave the following error. Argument of type 'E' is not assignable to parameter of type '{}'.ts(2769) index.ts(237, 36): This type parameter might need an `extends ArrayLike<unknown>` constraint. index.ts(237, 36): This type parameter might need an `extends { [s: string]: unknown; } | ArrayLike<unknown>` constraint.
尝试泛型约束后的测试错误
Error: projects/fs-collections/src/lib/src/index.spec.ts:154:41 - error TS2344: Type 'Student' does not satisfy the constraint '{ [s: string]: unknown; } | ArrayLike<unknown>'. Type 'Student' is not assignable to type '{ [s: string]: unknown; }'. Index signature for type 'string' is missing in type 'Student'. 154 let s:Students = toArrayByObjectKey<Student>(students);
解决方法
方案1:宽松约束+类型断言(快速修复)
给泛型E添加object约束,同时对Object.entries的参数做类型断言,绕过索引签名检查,无需修改Student类型:
export function toArrayByObjectKey<E extends object>(entities: E[]) { let res: Record<string, unknown[]> = {}; for (let obj of entities) // 断言为带字符串索引的类型,满足Object.entries的类型要求 for (let [k, v] of Object.entries(obj as Record<string, unknown>)) res[k] = (res[k] || []).concat(v); return res; };
方案2:精确返回类型(进阶优化)
使用TypeScript映射类型,让函数返回的对象类型与输入实体的属性严格对应,同时解决类型错误:
// 定义映射类型:将对象的每个属性转为对应类型的数组 type ObjectToArrays<T> = { [K in keyof T]: T[K][] }; export function toArrayByObjectKey<E extends object>(entities: E[]): ObjectToArrays<E> { let res = {} as ObjectToArrays<E>; for (let obj of entities) { // 遍历对象键并断言为E的键类型,保证类型安全 for (let k of Object.keys(obj) as Array<keyof E>) { const v = obj[k]; res[k] = (res[k] || []) as E[typeof k][]; res[k].push(v); } } return res; };
这种方案下,toArrayByObjectKey(students)会返回类型为{id: number[], name: string[], sex: string[], ...}的对象,类型检查更严格。
方案3:为Student添加索引签名(不推荐)
如果允许修改Student类型定义,可以添加字符串索引签名,但会破坏类型的精确性:
interface Student { id: number; name: string; sex: string; standard: number; propName: number; anotherSillyPropName: number; // 添加索引签名 [key: string]: unknown; }
内容的提问来源于stack exchange,提问作者Ole
相关产品推荐
相关产品推荐

