如何在TypeScript中实现类型安全的对象映射?
TypeScript对象映射:解决键类型丢失与可选属性问题
修复mapObject1:用类型断言补全键类型
Object.entries()会把对象的键统一转为string,导致Object.fromEntries()只能推断出字符串键的对象,和我们需要的{ [P in K]: B }类型不匹配。直接在返回结果上添加类型断言即可,这个断言是安全的——我们确实遍历了原对象的所有键,结构完全符合目标类型:
function mapObject1<K extends PropertyKey, A, B>( object: { [P in K]: A }, mapper: (value: A) => B, ): { [P in K]: B } { return Object.fromEntries( Object.entries(object) .map(([key, value]) => [key, mapper(value)]), ) as { [P in K]: B }; }
修复mapObject2:两种方法消除可选属性
方法1:断言结果为必选类型
虽然初始化时result是可选属性,但我们通过forEach遍历了原对象的所有键并完成赋值,所以可以直接断言结果为目标的必选类型:
export function mapObject2<K extends PropertyKey, A, B>( object: { [P in K]: A }, mapper: (value: A) => B, ): { [P in K]: B } { const result: { [P in K]?: B } = {}; (Object.keys(object) as K[]).forEach((key: K) => { result[key] = mapper(object[key]); }); return result as { [P in K]: B }; }
方法2:用reduce构建更贴合类型推断的对象(推荐)
换成reduce来构建结果,初始化时直接指定累加器的类型为目标类型,不需要声明可选属性,TypeScript能自动识别所有键都会被赋值:
export function mapObject2<K extends PropertyKey, A, B>( object: { [P in K]: A }, mapper: (value: A) => B, ): { [P in K]: B } { return (Object.keys(object) as K[]).reduce((acc, key) => { acc[key] = mapper(object[key]); return acc; }, {} as { [P in K]: B }); }
进阶:支持symbol类型的键
如果你的对象可能包含symbol类型的键,Object.keys()和Object.entries()会忽略它们。改用Reflect.ownKeys()可以遍历所有类型的键:
function mapObject<K extends PropertyKey, A, B>( object: { [P in K]: A }, mapper: (value: A) => B, ): { [P in K]: B } { const result = {} as { [P in K]: B }; for (const key of Reflect.ownKeys(object) as K[]) { result[key] = mapper(object[key]); } return result; }
这个版本兼容字符串、数字、symbol三种类型的键,适用性更广。
内容的提问来源于stack exchange,提问作者Jonan
相关产品推荐
相关产品推荐

