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

TypeScript中如何实现mapObject的mapper函数按key正确类型收窄

当前版本TypeScript(4.7及以上均支持)可以实现该需求,核心是将mapperFn的两个入参绑定为可辨识联合类型,而非独立的无关联联合类型,完整实现如下:

完整实现代码

// 定义对象键值对的可辨识联合类型,将key和对应value的类型绑定
type ObjectEntry<T> = {
  [K in keyof T]: [key: K, value: T[K]]
}[keyof T]

// 定义mapper函数类型:入参为关联绑定的key和value,返回新的键值对
type ObjectMapper<T, V> = (...args: ObjectEntry<T>) => [string, V]

function mapObject<T extends Record<string, any>, V>(
  obj: T,
  mapperFn: ObjectMapper<T, V>
): Record<string, V> {
  return Object.fromEntries(
    (Object.entries(obj) as Array<ObjectEntry<T>>).map(
      ([key, value]) => mapperFn(key, value)
    )
  ) as Record<string, V>
}

效果测试

interface TestI {
  str: string;
  num: number;
}

const test: TestI = { str: 'test', num: 123 }

const res = mapObject(test, (key, value) => {
  if (key === 'str') {
    // value自动收窄为string类型,可正常调用字符串方法
    return [key.toUpperCase(), value.length]
  } else if (key === 'num') {
    // value自动收窄为number类型,可正常调用数字方法
    return [key.toUpperCase(), value.toFixed(2)]
  } else {
    // key自动收窄为never类型
    throw new Error('unexpected key')
  }
})

// res的类型自动推导为Record<string, string | number>,符合预期
console.log(res) // 输出 { STR: 4, NUM: '123.00' }

实现原理

你之前的写法中key: keyof T和value: K[typeof key]是两个独立的联合类型,TypeScript无法建立两者的关联关系,因此判断key的类型后无法收窄value。而ObjectEntry<T>是绑定了对应key和value类型的可辨识联合,将其展开为mapperFn的入参后,TypeScript可以识别两个参数的关联关系,判断key类型后会自动收窄对应value的类型。该实现符合你提到的约束,仅处理对象显式定义的属性,不识别运行时可能存在的额外属性。

内容的提问来源于stack exchange,提问作者Abel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 15:24:05