如何将TypeScript多层索引器对象扁平化为键与属性结构?
如何将TypeScript多层索引器对象扁平化为键与属性结构?
嘿,我来帮你解决这个TypeScript对象扁平化的问题!先看看你给出的示例代码(看起来最后那段代码没写完,但核心结构我已经get到了):
const PluginFilterType = { boolean: 'boolean', string: 'string', date: 'date', } as const; const PluginFiltersMap = { project: { name: { type: PluginFilterType.string, label: 'Project Name', }, framework: { type: PluginFilterType.date, label: 'Framework', }, }, service: { name: { type: PluginFilterType.string, label: 'Service Name', }, // 这里应该还有其他属性,不过先按现有结构来处理 }, };
你想要把这种嵌套的两层结构,扁平化成类似{ "project.name": { type: "...", label: "..." } }这样的键值对结构,对吧?我分两步来给你讲怎么实现,连TypeScript的类型提示都给你安排得明明白白。
第一步:定义扁平化后的类型
首先得搞定TypeScript的类型定义,这样扁平化后的对象能有正确的类型提示,不会写代码的时候一脸懵:
// 定义扁平化后的键类型,自动拼接成"group.key"的格式 type FlattenedFilterKeys< T extends Record<string, Record<string, any>> > = `${keyof T & string}.${keyof T[keyof T] & string}`; // 定义扁平化后的完整对象类型 type FlattenedFilters< T extends Record<string, Record<string, any>> > = { [K in FlattenedFilterKeys<T>]: K extends `${infer Group}.${infer Key}` ? T[Group][Key] : never; };
这个类型会自动把外层键和内层键用点拼接起来,同时把对应的属性类型关联过去,后面写函数的时候TypeScript就会帮我们做类型检查啦。
第二步:实现扁平化函数
接下来就是写实际的扁平化函数,用两层循环就能轻松搞定,逻辑超清晰:
function flattenFilters< T extends Record<string, Record<string, any>> >(filters: T): FlattenedFilters<T> { const result: Partial<FlattenedFilters<T>> = {}; // 先遍历外层的分组,比如project、service for (const group in filters) { if (Object.prototype.hasOwnProperty.call(filters, group)) { const groupFilters = filters[group]; // 再遍历每个分组里的具体筛选器 for (const key in groupFilters) { if (Object.prototype.hasOwnProperty.call(groupFilters, key)) { const fullKey = `${group}.${key}` as FlattenedFilterKeys<T>; result[fullKey] = groupFilters[key]; } } } } return result as FlattenedFilters<T>; }
试试效果
现在直接调用这个函数,把你的PluginFiltersMap传进去就行:
const flattenedFilters = flattenFilters(PluginFiltersMap); // 现在访问的时候TypeScript会有完整的类型提示哦 console.log(flattenedFilters['project.name'].label); // 输出 "Project Name" console.log(flattenedFilters['service.name'].type); // 输出 "string"
如果你的对象以后有更多层级(比如三层甚至更深),可以把这个函数改成递归版本的,不过看你现在的需求是两层结构,这个实现完全够用啦。
备注:内容来源于stack exchange,提问作者Ori Pinhas
相关产品推荐
相关产品推荐

