TypeScript泛型第二个参数无法收窄的类型推导问题求助
解决TypeScript泛型参数推断与可选属性生成问题
问题分析
你当前的代码中,Defaults的约束定义过于严格:
Defaults extends { [I in keyof Objs]: { [K in keyof Objs[I]]: keyof Objs[I][K] } }
这个约束要求Defaults的每个元素必须包含对应Objs[I]的所有键,但你需要允许defaults为空对象或仅包含部分键,因此TypeScript会强制推断出包含所有键的类型(比如[{test: "a" | "b"}]),而非期望的[{}]。
解决方案
我们可以通过以下步骤修复推断问题,并实现最终的类型生成逻辑:
1. 定义单个配置项的类型约束
首先明确每个输入参数的结构,让defaults成为obj键的可选映射:
// 单个配置项类型:obj是指定结构,defaults是obj键的可选映射(值为对应子对象的键) type Config<O extends Record<string, Record<string, number>>> = { obj: O; defaults: Partial<{ [K in keyof O]: keyof O[K] }>; };
2. 调整泛型函数定义
改用单元素泛型的数组形式,让TypeScript更准确地推断每个参数的类型,并实现结果类型的合并:
// 辅助类型:将联合类型转为交叉类型,用于合并所有obj的键 type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; // 辅助类型:判断某个键是否在任意配置项的defaults中存在(用于标记可选) type IsOptional<T extends Config<any>[], K extends PropertyKey> = T[number] extends { obj: Record<K, any>, defaults: infer D } ? (K extends keyof D ? true : false) : false; // 核心类型:合并所有配置项,生成最终结果类型 type MergeConfigs<T extends Config<any>[]> = { // 先定义所有必填键的类型 [K in keyof UnionToIntersection<T[number]["obj"]>]: T[number] extends { obj: Record<K, infer V> } ? keyof V : never; } & { // 再叠加可选键的类型(覆盖必填定义) [K in keyof UnionToIntersection<T[number]["obj"]> as IsOptional<T, K> extends true ? K : never]?: T[number] extends { obj: Record<K, infer V> } ? keyof V : never; }; // 最终函数 const g = <T extends Config<any>[]>(...props: T): MergeConfigs<T> => { // 这里可以添加运行时逻辑,比如合并默认值等 return {} as MergeConfigs<T>; };
3. 测试验证
- 输入1:
g({ obj: { test: { a: 1, b: 2 } }, defaults: {} }); // 返回类型:{ test: "a" | "b" } - 输入2:
g({ obj: { test: { a: 1, b: 2 } }, defaults: { test: "b" } }); // 返回类型:{ test?: "a" | "b" } - 输入3:
g( { obj: { test: { a: 1, b: 2 } }, defaults: {} }, { obj: { test2: { a: 2, d: 5 } }, defaults: { test2: "a" } } ); // 返回类型:{ test: "a" | "b", test2?: "a" | "d" }
关键改进点
- 将
Defaults的约束从必填全量键改为Partial可选映射,允许空对象或部分键的输入; - 用
UnionToIntersection合并所有输入obj的键,确保结果类型包含所有属性; - 通过
IsOptional判断哪些属性需要标记为可选,实现核心逻辑。
内容的提问来源于stack exchange,提问作者Niik
相关产品推荐
相关产品推荐

