如何在TypeScript中实现对象字段的类型安全映射?
TypeScript对象字段类型映射实现方案
问题分析
你之前的实现中,DataReturn<T>里的infer Item会把输入对象所有字段的类型合并成一个联合类型,导致每个字段的返回类型变成所有可能类型的集合。正确的思路是直接对每个字段做针对性的类型映射,不需要用infer。
基础实现:正确的类型映射
我们可以用TypeScript的映射类型直接遍历输入对象的每个键,根据对应的值类型映射到目标类型,同时用类型映射表简化维护:
// 定义输入类型标识到返回类型的映射表,新增类型只需在这里加键值对 type TypeMap = { 'string': string; 'boolean': boolean; 'number': number; }; // 输入对象类型:键为任意字符串,值为TypeMap的键 type DataDefinition = { [K: string]: keyof TypeMap }; // 返回类型:对输入对象的每个键K,返回TypeMap中对应的类型 type DataReturn<T extends DataDefinition> = { [K in keyof T]: TypeMap[T[K]]; }; const registerData = <T extends DataDefinition>(data: T): DataReturn<T> => { const result = {} as DataReturn<T>; // 模拟根据类型生成数据的逻辑 for (const key in data) { const type = data[key]; switch (type) { case 'string': result[key] = 'Lorem ipsum dolor sit amet' as TypeMap['string']; break; case 'boolean': result[key] = Math.random() > 0.5 as TypeMap['boolean']; break; case 'number': result[key] = Math.floor(Math.random() * 100) as TypeMap['number']; break; } } return result; }; // 测试调用 const data = registerData({ bodyText: 'string', isAdmin: 'boolean', postCount: 'number' }); // 此时data的类型完全符合预期: // { // bodyText: string; // isAdmin: boolean; // postCount: number; // }
扩展:支持更多类型与自定义返回
如果需要区分类似int和float(最终都返回number[]),或者返回函数、数组等任意类型,只需扩展TypeMap和对应的生成逻辑即可:
// 扩展类型映射表,支持任意返回类型 type TypeMap = { 'string': string; 'boolean': boolean; 'number': number; 'int': number[]; // int类型返回数字数组 'float': number[]; // float类型也返回数字数组 'callback': () => void; // 返回函数类型 }; type DataDefinition = { [K: string]: keyof TypeMap }; type DataReturn<T extends DataDefinition> = { [K in keyof T]: TypeMap[T[K]]; }; const registerData = <T extends DataDefinition>(data: T): DataReturn<T> => { const result = {} as DataReturn<T>; for (const key in data) { const type = data[key]; switch (type) { case 'string': result[key] = 'Lorem ipsum dolor sit amet' as TypeMap['string']; break; case 'boolean': result[key] = Math.random() > 0.5 as TypeMap['boolean']; break; case 'number': result[key] = Math.floor(Math.random() * 100) as TypeMap['number']; break; case 'int': result[key] = [1, 2, 3] as TypeMap['int']; break; case 'float': result[key] = [1.1, 2.2, 3.3] as TypeMap['float']; break; case 'callback': result[key] = () => console.log('Hello') as TypeMap['callback']; break; } } return result; }; // 测试扩展类型 const extendedData = registerData({ ids: 'int', prices: 'float', log: 'callback' }); // extendedData的类型: // { // ids: number[]; // prices: number[]; // log: () => void; // }
原方案失败原因
你之前用T extends { [K in keyof T]: infer Item }时,infer Item会把T所有字段的类型合并成一个联合类型(比如'string' | 'boolean' | 'number'),然后每个字段都用这个联合类型去匹配DataReturnItem,最终导致每个字段的类型变成string | boolean | number。而直接用映射类型[K in keyof T]: TypeMap[T[K]]会逐个处理每个字段的具体类型,得到精准的映射结果。
内容的提问来源于stack exchange,提问作者Alex Davies
相关产品推荐
相关产品推荐

