如何将带点的对象键转为嵌套对象并保留值类型?
实现带点分隔键的对象转嵌套对象并保留TypeScript类型
要实现将扁平的带点分隔键对象转换为嵌套对象,同时完整保留值的类型,需要从类型定义和运行时逻辑两部分入手:
1. 定义类型转换工具
首先编写TypeScript类型工具,把扁平的BasicFields类型转换为嵌套结构类型:
拆分键字符串的类型
// 递归拆分带点分隔的键为字符串数组,比如"alt.number" → ["alt", "number"] type SplitKey<K extends string> = K extends `${infer Prefix}.${infer Suffix}` ? [Prefix, ...SplitKey<Suffix>] : [K];
构建嵌套类型
// 将扁平类型转换为嵌套类型 type Nestedify<T> = { [K in keyof T as SplitKey<K & string>[0]]: // 如果键拆分后只有一层,直接取原类型的值 SplitKey<K & string> extends [infer _SingleKey] ? T[K] : // 否则递归处理剩余的键,构建嵌套子对象 Nestedify<{ [SubKey in K as SplitKey<K & string>[1]]: T[K] }> };
通过Nestedify<LocationSchemas.BasicFields>,就能得到期望的嵌套类型结构:
{ name: LocationSchemas.FieldProps; region: LocationSchemas.Region; types: LocationSchemas.Types; alt?: { number?: LocationSchemas.FieldProps; centreNumber?: LocationSchemas.FieldProps; }; typeDetails: { available: LocationSchemas.TypeDetails; virtual: LocationSchemas.TypeDetails; }; internal: { name?: LocationSchemas.FieldProps; type: LocationSchemas.FieldProps; format?: LocationSchemas.FieldProps; status: LocationSchemas.InternalTradingStatus; }; }
2. 修改运行时转换函数
调整remapKeys函数,支持任意层级的键拆分,并返回正确的嵌套类型:
const remapKeys = <T extends Record<string, unknown>>(fields: T): Nestedify<T> => { return Object.entries(fields).reduce((result, [key, value]) => { const keySegments = key.split("."); let currentLayer = result; // 遍历键的每一段,逐层构建嵌套对象 for (let i = 0; i < keySegments.length; i++) { const segment = keySegments[i]; if (i === keySegments.length - 1) { // 最后一段直接赋值 (currentLayer as Record<string, unknown>)[segment] = value; } else { // 非最后一段,若当前层无该属性则初始化空对象 if (!(segment in currentLayer)) { (currentLayer as Record<string, unknown>)[segment] = {}; } currentLayer = currentLayer[segment] as Record<string, unknown>; } } return result; }, {} as Nestedify<T>); };
3. 验证效果
使用修改后的函数处理原数据,remappedData会自动获得正确的嵌套类型,并且运行时结构符合预期:
const remappedData = remapKeys(data); // 示例访问: console.log(remappedData.alt?.number?.visible); // 类型安全,提示正确属性 console.log(remappedData.internal.status.possibleValues); // 类型完全保留
内容的提问来源于stack exchange,提问作者Mugetsu
相关产品推荐
相关产品推荐

