TypeScript如何通过映射类型正确推断合并对象数组后的返回类型
问题原因
原有写法的核心问题有两点:
- 泛型
D的默认值设为ThemeDependency[],TypeScript无法自动推断传入的dependencies数组的具体元组类型,只会将其当做普通的ThemeDependency数组处理,因此读取到的defaults.size类型就是基类ThemeDependency对应的未知类型,最终推导为unknown - 额外定义的泛型
A默认值为number,无法对应到数组的具体元素索引,即使手动传入数字也无法拿到准确的元素类型
解决方案
1. 定义递归合并工具类型
遍历依赖数组的所有元素,把每个元素的defaults.size做交叉合并,得到最终的components类型:
type MergeDependencySizes<D extends ThemeDependency[]> = // 递归拆分元组,合并每一项的size属性 D extends [infer First extends ThemeDependency, ...infer Rest extends ThemeDependency[]] ? First['defaults']['size'] & MergeDependencySizes<Rest> // 递归结束返回空对象 : {}
2. 改造函数泛型定义
去掉无意义的A泛型,让TypeScript自动推断传入的依赖数组的具体元组类型:
// 不要给D设默认值,让TS自动推断传入的数组具体类型 const stitchTheme = < T extends RawTheme, D extends ThemeDependency[] >( rawTheme: T, dependencies: D, options?: StitchThemeOptions ): T & { size: T['size'] & { components: MergeDependencySizes<D> } } => { // 这里写你的实际合并逻辑,把所有dependencies的defaults.size合并到theme.size.components下 const merged = { ...rawTheme } merged.size = { ...merged.size, components: {} } dependencies.forEach(dep => { Object.assign(merged.size.components, dep.defaults.size) }) return merged as any }
使用效果
调用时不需要手动传入泛型,TS会自动完成类型推断:
const finalTheme = stitchTheme(theme, [barDependency]) // 此时finalTheme.size.components.bar.foo会被正确推断为number类型 console.log(finalTheme.size.components.bar.foo) // 12
如果你的依赖数组是动态生成的、不是常量元组,可以给ThemeDependency接口加明确的类型约束,避免推断为unknown:
interface ThemeDependency { defaults: { size: Record<string, any> color: Record<string, any> } }
内容的提问来源于stack exchange,提问作者Shaegi
相关产品推荐
相关产品推荐

