TypeScript自定义Object.entries/fromEntries类型推导问题求助
解决Object.entries/fromEntries增强类型中map的类型推导问题
问题分析
你当前的工具类已经实现了entries和fromEntries的基础类型增强,但在使用map处理entries时,无法保留key与新value的类型关联——所有属性类型会被合并成联合类型,核心原因在于:
updateEntryVal直接接收独立的NewValue参数,TypeScript无法将其与每个entry的具体key/原始值类型绑定- 处理联合类型的entry时,缺乏针对每个分支的单独类型推导逻辑
修正方案
以下是调整后的工具类代码,重点优化了updateEntryVal的类型推导逻辑,确保map后仍保留key与新value的精准关联:
// 保留Entry的Key,替换为新的Value类型 type TransformEntry<Entry extends [string, unknown], NewValue> = Entry extends [infer K, infer _] ? [K, NewValue] : never; export class Obj { // 增强版entries:返回带key-value类型关联的联合数组 static entries<T extends object>(obj: T): Array<{ [K in keyof T]: [K, T[K]] }[keyof T]> { return Object.entries(obj) as Array<{ [K in keyof T]: [K, T[K]] }[keyof T]>; } // 增强版fromEntries:从entry数组推导精准的对象类型 static fromEntries<T extends readonly [string, unknown][]>(entries: T): { [K in T[number][0]]: Extract<T[number], [K, unknown]>[1] } { return Object.fromEntries(entries) as { [K in T[number][0]]: Extract<T[number], [K, unknown]>[1] }; } // 优化后的updateEntryVal:通过转换函数保留key与新value的类型关联 static updateEntryVal = <Entry extends [string, unknown], NewVal>( entry: Entry, transform: (originalVal: Entry[1]) => NewVal ): TransformEntry<Entry, NewVal> => { const [key, val] = entry; return [key, transform(val)] as TransformEntry<Entry, NewVal>; }; }
关键改进点
- 转换函数替代直接传值:将
updateEntryVal改为接收转换函数,让TypeScript能针对每个entry分支的原始值类型,推导对应新值的类型,从而保留key与新value的绑定关系 - 冗余类型清理:去掉
TransformEntry中无意义的Extract<Val, Val>,直接返回[K, NewValue]
测试验证
所有示例现在都能精准推导类型:
示例1:统一更新为数值
const obj1 = { a: "hello", b: true }; const updatedEntries1 = Obj.entries(obj1).map(entry => Obj.updateEntryVal(entry, () => 123)); const newObj1 = Obj.fromEntries(updatedEntries1); // newObj1类型:{ a: number; b: number } ✅
示例2:保留原值
const obj2 = { a: "hello", b: true }; const updatedEntries2 = Obj.entries(obj2).map(entry => Obj.updateEntryVal(entry, val => val)); const newObj2 = Obj.fromEntries(updatedEntries2); // newObj2类型:{ a: string; b: boolean } ✅ 无联合类型污染
示例3:函数转换值
const obj3 = { a: 1, b: "test" }; const updatedEntries3 = Obj.entries(obj3).map(entry => Obj.updateEntryVal(entry, val => typeof val === "number" ? val * 2 : val.toUpperCase()) ); const newObj3 = Obj.fromEntries(updatedEntries3); // newObj3类型:{ a: number; b: string } ✅ 每个属性类型匹配转换逻辑
示例4:值包装为对象
const obj4 = { a: 1, b: "test" }; const updatedEntries4 = Obj.entries(obj4).map(entry => Obj.updateEntryVal(entry, val => ({ value: val })) ); const newObj4 = Obj.fromEntries(updatedEntries4); // newObj4类型:{ a: { value: number }; b: { value: string } } ✅ 包装类型与原始值关联
内容的提问来源于stack exchange,提问作者Jonathan002
相关产品推荐
相关产品推荐

