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
相关产品推荐
相关产品推荐

