You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript自定义Object.entries/fromEntries类型推导问题求助

解决Object.entries/fromEntries增强类型中map的类型推导问题

问题分析

你当前的工具类已经实现了entries和fromEntries的基础类型增强,但在使用map处理entries时,无法保留key与新value的类型关联——所有属性类型会被合并成联合类型,核心原因在于:

  1. updateEntryVal直接接收独立的NewValue参数,TypeScript无法将其与每个entry的具体key/原始值类型绑定
  2. 处理联合类型的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>;
  };
}

关键改进点

  1. 转换函数替代直接传值:将updateEntryVal改为接收转换函数,让TypeScript能针对每个entry分支的原始值类型,推导对应新值的类型,从而保留key与新value的绑定关系
  2. 冗余类型清理:去掉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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 09:07:38