TypeScript实现带父键前缀的嵌套对象/函数扁平化及类型定义
带TypeScript类型支持的嵌套命令对象扁平化方案
需求说明
现有一个嵌套结构的TypeScript对象,内部包含函数和子对象:
const commands = { doSomething: () => 'Yo', table: { rows: () => 10, columns: () => 6, name: (name: string) => name + ' hello' }, doSomethingElse: () => 'Boo', modal: { sub: { hello: () => 'hello' } } } as const;
需要将其扁平化为键名由父键加下划线拼接的对象,同时保留完整的TypeScript类型支持,最终效果如下:
const mappedCommands = { doSomething: () => 'Yo', table_rows: () => 10, table_columns: () => 6, table_name: (name: string) => name + ' hello', doSomethingElse: () => 'Boo', modal_sub_hello: () => 'hello' }
现有实现的createCommands函数仅能完成扁平化,但无法保留类型信息,需要改造为带类型支持的版本。
解决方案
1. 定义递归扁平化类型工具
先实现一个递归的类型工具,用来将嵌套对象的类型转换为扁平化后的类型:
// 递归扁平化命令对象的类型 type FlattenCommands<T, Prefix extends string = ''> = T extends (...args: any[]) => any ? { [K in Prefix extends '' ? keyof T : never]: T } // 顶层函数直接保留 : { [K in keyof T as T[K] extends (...args: any[]) => any ? `${Prefix}${Extract<K, string>}` : `${Prefix}${Extract<K, string>}_${keyof FlattenCommands<T[K]> & string}` ]: T[K] extends (...args: any[]) => any ? T[K] : FlattenCommands<T[K]>[keyof FlattenCommands<T[K]>] };
2. 改造createCommands函数
调整函数的泛型定义,让返回值类型匹配扁平化后的类型,同时优化实现逻辑:
function createCommands<T, Prefix extends string = ''>( inputs: T, prepend: Prefix = '' as Prefix ): FlattenCommands<T, Prefix> { const mappedCommands = {} as FlattenCommands<T, Prefix>; Object.entries(inputs as Record<string, any>).forEach(([key, value]) => { if (typeof value === 'function') { const flatKey = `${prepend}${key}` as keyof FlattenCommands<T, Prefix>; mappedCommands[flatKey] = value as any; } else { const subPrefix = `${prepend}${key}_`; const subCommands = createCommands(value, subPrefix); Object.assign(mappedCommands, subCommands); } }); return mappedCommands; }
3. 使用示例
调用改造后的函数,即可得到带完整类型支持的扁平化对象:
// 调用函数得到扁平化对象 const mappedCommands = createCommands(commands); // 验证类型 type Commands = typeof mappedCommands; /* Commands类型会被正确推导为: { doSomething: () => "Yo"; table_rows: () => 10; table_columns: () => 6; table_name: (name: string) => string; doSomethingElse: () => "Boo"; modal_sub_hello: () => "hello"; } */
内容的提问来源于stack exchange,提问作者Ewan
相关产品推荐
相关产品推荐

