You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 17:35:36