能否通过返回值类型动态映射对象类型并实现类型推断?
TypeScript 对象值映射的类型推断优化
需求
需要将类型 A 的对象转换为类型 B:
type A = { name: string; age: number; dictionary: { type: 'Dictionary'; id: string; name: string } } type B = { name: string; age: number; dictionary: { label: string; value: string } }
现有问题
使用 Lodash mapValues 实现的转换逻辑无法让 TypeScript 正确推断输入输出类型,导致代码没有类型提示,返回值类型不符合预期:
type Dictionary = { type: 'Dictionary'; id: string; name: string }; const mapDictionary = ({ id, name }: Dictionary) => ({ label: name, value: id }); // 原代码存在笔误:fieldValue应为value const cb = (value: string | boolean | number | null | Dictionary) => { if (typeof value === 'string') return value; if (typeof value === 'boolean') return value; if (typeof value === 'number') return value; if (value === null) return null; if ('type' in value) return mapDictionary(value); return value; }; const mapData = (data) => { if (!data) return {}; return mapValues(data, cb); };
解决方案
通过泛型类型映射+泛型函数实现自动类型推断,同时保留原对象的键结构:
1. 定义值转换的泛型类型
先声明一个泛型类型,描述单个值的转换规则:
type MapValueType<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T extends null ? null : T extends Dictionary ? { label: string; value: string } : T;
2. 定义对象级别的转换泛型
遍历原对象的所有键,为每个值应用上述转换规则:
type MapObject<T> = { [K in keyof T]: MapValueType<T[K]>; };
3. 改造为泛型工具函数
给 mapData 和回调函数添加泛型约束,让 TypeScript 自动推断类型:
import { mapValues } from 'lodash'; type Dictionary = { type: 'Dictionary'; id: string; name: string }; type MapValueType<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T extends null ? null : T extends Dictionary ? { label: string; value: string } : T; type MapObject<T> = { [K in keyof T]: MapValueType<T[K]>; }; const mapDictionary = ({ id, name }: Dictionary) => ({ label: name, value: id }); const cb = <T>(value: T): MapValueType<T> => { // 合并基础类型判断 if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { return value as MapValueType<T>; } if (value === null) { return null as MapValueType<T>; } // 更精确的类型守卫,确保是Dictionary类型 if ('type' in value && value.type === 'Dictionary') { return mapDictionary(value) as MapValueType<T>; } return value as MapValueType<T>; }; const mapData = <T extends Record<string, any>>(data: T | null | undefined): MapObject<T> => { if (!data) { return {} as MapObject<T>; } return mapValues(data, cb) as MapObject<T>; };
验证效果
现在调用 mapData 时,TypeScript 会自动推断输入对象的类型,并返回正确的映射后类型:
const input: A = { name: 'Alice', age: 30, dictionary: { type: 'Dictionary', id: 'dict_001', name: '测试字典' } }; const output = mapData(input); // output的类型会被自动推断为B,拥有完整的类型提示
内容的提问来源于stack exchange,提问作者Bruce Lee
相关产品推荐
相关产品推荐

