TypeScript泛型嵌套属性类型推断:类型保留与字面量值问题
对象嵌套键下划线转换的类型修复方案
问题背景
需要实现对象结构转换:仅给嵌套对象的键添加前缀下划线,例如将{ isElevated: { sm: true, xl: 'false' }, color: 'green' }转为{ isElevated: { _sm: true, _xl: 'false' }, color: 'green' }。现有代码存在两个核心问题:
- 处理嵌套属性时丢失类型信息;
- 值类型被自动拓宽(如字面量
'green'被推断为宽泛的string类型),希望无需手动添加as const就能保留原始字面量类型。
修复后的代码实现
// 定义转换类型:仅给一层嵌套对象的键添加下划线(如需深层转换可改为递归) type TransformProps<T> = { [K in keyof T]: T[K] extends object ? { [NK in keyof T[K] as `_${string & NK}`]: T[K][NK]; } : T[K]; }; // 转换函数:自动推断字面量类型,修复嵌套属性类型丢失问题 function transform<T extends object>(props: T): TransformProps<T> { const transformed: Partial<TransformProps<T>> = {}; for (const key in props) { if (Object.prototype.hasOwnProperty.call(props, key)) { const value = props[key]; // 处理嵌套对象(排除null,因为typeof null === 'object') if (typeof value === 'object' && value !== null) { const nestedTransformed: Record<string, unknown> = {}; for (const nestedKey in value) { if (Object.prototype.hasOwnProperty.call(value, nestedKey)) { nestedTransformed[`_${nestedKey}`] = value[nestedKey]; } } // 类型断言确保转换后的嵌套结构符合TransformProps定义 transformed[key as keyof TransformProps<T>] = nestedTransformed as TransformProps<T>[keyof TransformProps<T>]; } else { // 直接赋值非嵌套属性,保留原始类型 transformed[key as keyof TransformProps<T>] = value as TransformProps<T>[keyof TransformProps<T>]; } } } return transformed as TransformProps<T>; } // 测试示例 const result = transform({ isElevated: { sm: true, xl: 'false' }, color: 'green', }); const success1 = result.isElevated["_sm"] === true; // 类型校验通过,无报错 // 现在这些错误会被TypeScript正确捕获 const fail1 = result.isElevated["_sm"] === false; // TS报错:类型“true”不能赋值给类型“false” const fail2 = result.color === "red"; // TS报错:类型“"green"”不能赋值给类型“"red"”
关键修复点说明
自动保留字面量类型
调整函数泛型为T extends object,让TypeScript直接基于传入的对象字面量推断精确的字面量类型,无需手动添加as const强制约束。解决嵌套属性类型丢失
- 替换
reduce为for...in循环,更直观地逐个处理每个属性,避免类型推断模糊; - 对嵌套对象的转换结果使用类型断言,确保其与
TransformProps定义的类型结构完全对齐; - 用
Object.prototype.hasOwnProperty.call过滤原型链属性,避免遍历到非目标属性导致类型混乱。
- 替换
扩展:支持深层嵌套转换
如果需要对多层嵌套对象的键都添加下划线,可以将TransformProps改为递归类型:
type TransformProps<T> = { [K in keyof T]: T[K] extends object ? TransformProps<{ [NK in keyof T[K] as `_${string & NK}`]: T[K][NK] }> : T[K]; };
内容的提问来源于stack exchange,提问作者wolvesback019
相关产品推荐
相关产品推荐

