使用reduce向空对象添加键时的TypeScript类型问题
TypeScript:修复不可变对象处理函数中的隐式any类型错误
问题场景
编写了不可变对象处理函数reduceObjectImmutable,使用reduce将筛选后的键值对赋值给累加器acc时,acc[key]与object[key]均触发类型错误,提示因string类型无法索引空对象导致隐式any类型,要求不使用any解决该问题。
报错信息:
acc[key]
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.object[key]
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
No index signature with a parameter of type 'string' was found on type '{}'
原代码:
// Reduce Object Immutable !! V2 dont use the delete operator which is slow. export const reduceObjectImmutable = <T extends object>( arr: string[], obj: T ) => { // make a shallow copy const object = { ...obj }; // get all object keys const keys = Object.keys(object); // filter keys that are not in the remove array const filteredKeys = keys.filter((key) => !arr.includes(key)); // create new object with filtered keys const newObject = filteredKeys.reduce((acc, key) => { // Error on both... acc[key] = object[key]; return acc; }, {}); return newObject; };
问题原因
Object.keys()返回的类型是string[],而非keyof T,导致key被推断为string类型,无法直接用来索引类型为T的object和初始值为{}的acc。- 累加器
acc的初始值{}类型为{},TypeScript不允许用string类型的键去索引该类型,因此触发隐式any错误。
解决方案
方案一:修正reduce中的类型断言与泛型
通过类型断言明确键的类型,并指定累加器的正确类型,避免隐式any:
// Reduce Object Immutable !! V2 dont use the delete operator which is slow. export const reduceObjectImmutable = <T extends object>( arr: string[], obj: T ) => { const object = { ...obj }; // 将Object.keys结果断言为T的键数组 const keys = Object.keys(object) as Array<keyof T>; // 过滤掉需要移除的键,将key转为string匹配arr的类型 const filteredKeys = keys.filter((key) => !arr.includes(key as string)); // 指定累加器类型为T的子集,避免索引错误 const newObject = filteredKeys.reduce((acc, key) => { acc[key] = object[key]; return acc; }, {} as Pick<T, keyof T>); // 返回更精确的类型:排除arr中存在的键后的T子集 return newObject as Pick<T, Exclude<keyof T, typeof arr[number]>>; };
方案二:使用Object.fromEntries简化实现
用Object.entries和Object.fromEntries替代reduce,代码更简洁且类型推断更友好:
export const reduceObjectImmutable = <T extends object>( arr: string[], obj: T ) => { return Object.fromEntries( Object.entries(obj).filter(([key]) => !arr.includes(key)) ) as Pick<T, Exclude<keyof T, typeof arr[number]>>; };
关键说明
- 两种方案都未使用
any,而是通过类型断言和精确泛型定义让TypeScript正确识别对象的键类型。 - 返回类型使用
Pick<T, Exclude<keyof T, typeof arr[number]>>,明确表示返回的是原对象排除指定键后的子集,比泛泛的object类型更具类型安全性。
内容的提问来源于stack exchange,提问作者Richardson
相关产品推荐
相关产品推荐

