TypeScript中if分支内泛型未正确推导类型问题求助
解决TypeScript递归Map转对象函数的泛型推导问题
问题根源
当通过itemToConvert instanceof Map判断输入为Map类型时,TypeScript无法自动将泛型参数T缩小为仅Map类型,导致acc被推断为object | unknown[] | Record<string | number | symbol, unknown>联合类型,进而触发索引类型错误。我们需要手动缩小类型范围,让TS明确此时的返回值应为Record类型。
修改后的完整代码
type InputMapToObject = | Map<keyof any, unknown> | Record<keyof any, unknown> | unknown[]; type ReturnMapToObject<T extends InputMapToObject> = T extends Map< infer KMap extends keyof any, infer VMap > ? Record< KMap, VMap extends InputMapToObject ? ReturnMapToObject<VMap> : VMap > : T extends object ? { [P in keyof T]: T[P] extends InputMapToObject ? ReturnMapToObject<T[P]> : T[P]; } : T extends (infer VArray)[] ? VArray extends InputMapToObject ? ReturnMapToObject<VArray>[] : VArray[] : never; export function mapToObject<T extends InputMapToObject>( itemToConvert: T ): ReturnMapToObject<T> { // 处理Map类型:手动缩小类型范围,明确acc为Record类型 if (itemToConvert instanceof Map) { const map = itemToConvert as Map<keyof any, unknown>; return [...map.entries()].reduce((acc, [key, value]) => { acc[key] = isRecursiveValue(value) ? mapToObject(value) : value; return acc; }, {} as ReturnMapToObject<typeof map>) as ReturnMapToObject<T>; } // 处理数组类型:断言map结果匹配返回类型 if (Array.isArray(itemToConvert)) { return itemToConvert.map((item) => isRecursiveValue(item) ? mapToObject(item) : item ) as ReturnMapToObject<T>; } // 处理普通对象类型 return getObjectEntries(itemToConvert).reduce((acc, [key, value]) => { acc[key] = isRecursiveValue(value) ? mapToObject(value) : value; return acc; }, {} as ReturnMapToObject<T>); } // 优化类型守卫,避免any类型 function isRecursiveValue(item: unknown): item is InputMapToObject { return item instanceof Map || Array.isArray(item) || (typeof item === 'object' && item !== null); } function getObjectEntries<T extends Record<any, any>>( value: T ): [keyof any, T[keyof T]][] { return Object.entries(value) as [keyof any, T[keyof T]][]; }
关键调整说明
Map分支类型缩小
将itemToConvert断言为Map<keyof any, unknown>,并将reduce初始值acc断言为ReturnMapToObject<typeof map>。此时TS能识别acc是Record类型(对应ReturnMapToObject的Map分支定义),解决了索引错误。最后将结果断言为ReturnMapToObject<T>,由于此时T必然是Map子类型,该断言安全有效。递归处理嵌套值
在所有分支中补充了对嵌套值的递归转换,确保嵌套的Map、对象或数组也能被正确处理,符合递归函数的设计需求。优化类型守卫
将isRecursiveValue的参数类型从any改为unknown,并简化判断逻辑,提升类型安全性。
测试验证
interface TestingType { testMap: Map<string, number>; testArray: Array<number>; testArray2: Array<Map<string, string>>; nested: { innerMap: Map<number, boolean>; }; } const input: TestingType = { testMap: new Map([['string', 0]]), testArray: [0], testArray2: [new Map([['string', 'string']])], nested: { innerMap: new Map([[1, true]]) } }; const output = mapToObject(input); // 输出类型自动推导为: // { // testMap: Record<string, number>; // testArray: number[]; // testArray2: Record<string, string>[]; // nested: { innerMap: Record<number, boolean> }; // }
内容的提问来源于stack exchange,提问作者Jordi Baliellas Portet
相关产品推荐
相关产品推荐

