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

TypeScript内联泛型条件类型推导结果异常的原因咨询

TypeScript泛型条件类型行为差异问题

问题背景

我是TypeScript初学者,在做类型编程时遇到了一个无法合理解释的泛型行为差异,具体场景如下:
首先定义了如下结构的对象:

const data = {
    "a": {
        include: true,
    },
    "b": {
        include: false,
    },
};

需求是通过类型编程,提取所有include属性值为true对应的键组成的类型,本例中预期得到类型"a"。我先后做了三次实现尝试,仅第一种写法可得到预期结果,另外两种均返回never类型。
核心困惑点:为什么将Attempt1中IncludedKey2的定义内联后(即Attempt2的写法),推导结果会发生变化。我能理解Attempt2和Attempt3的失败表面原因:(typeof data)[Keys]["include"]的类型为boolean,不满足extends true的约束,但始终不清楚为什么Attempt1可以正常工作。
相关实现代码如下:

const data = {
    "a": {
        include: true,
    },
    "b": {
        include: false,
    },
} as const;

type Keys = keyof (typeof data) // 推导结果:Keys = "a" | "b"
type IncludedKey<Id extends Keys> =
    (typeof data)[Id]["include"] extends true
        ? Id : never

// 用条件类型而非约束类型参数实现的版本
type IncludedKey2<Id> = Id extends Keys ? IncludedKey<Id> : never

// 正常工作
type Attempt1 = IncludedKey2<Keys>; // 推导结果:Attempt1 = "a"

// 推导失败
// 直接内联IncludedKey2的逻辑
type Attempt2 = Keys extends Keys ? IncludedKey<Keys> : never // 推导结果:Attempt2 = never
type Attempt3 = IncludedKey<Keys> // 推导结果:Attempt3 = never

核心原因:条件类型的分布式触发规则

三种写法的结果差异,完全来自TypeScript条件类型的联合类型分布式行为,触发该行为有且只有一个前提:

条件类型中extends关键字左侧是裸类型参数(即泛型定义中没有被任何类型构造器包裹的类型变量)。
满足该前提时,传入联合类型会被自动拆分为单个成员逐个执行条件判断,最后将所有判断结果合并为新的联合类型;不满足时,会将传入的类型作为整体做判断,不会拆分。

为什么Attempt1能得到正确结果

IncludedKey2<Id>的定义中,extends左侧是裸类型参数Id,当传入联合类型Keys = "a" | "b"时,会触发分布式逻辑:

  • 将联合拆分为单个成员"a"、"b"分别代入判断
  • 对"a":"a" extends Keys成立,继续计算IncludedKey<"a">,此时(typeof data)["a"]["include"]类型为true,满足extends true约束,返回"a"
  • 对"b":"b" extends Keys成立,继续计算IncludedKey<"b">,此时(typeof data)["b"]["include"]类型为false,不满足约束,返回never
  • 合并结果:"a" | never 等价于 "a",符合预期。

为什么Attempt2、Attempt3返回never

这两种写法都不满足分布式触发条件:

  • Attempt3直接将整个联合类型Keys作为参数传入IncludedKey,IncludedKey内部extends左侧是索引访问表达式(typeof data)[Id]["include"],Id在这里被传入的具体联合类型替换,不是泛型定义阶段的裸类型参数,不会触发拆分
  • Attempt2中Keys extends Keys的左侧是具体类型Keys,不是泛型类型参数,哪怕结构和IncludedKey2一致,也不会触发分布式逻辑
    两种写法都会把"a" | "b"作为整体计算:(typeof data)["a" | "b"]["include"]的结果是所有对应属性类型的联合,即true | false = boolean,而boolean extends true不成立,因此最终返回never。

正确的内联写法

如果不想额外定义IncludedKey2,可以通过infer引入泛型语境下的裸类型参数触发分布式,写法如下:

type InlineAttempt = Keys extends infer K extends Keys ? IncludedKey<K> : never
// 推导结果为"a"

内容的提问来源于stack exchange,提问作者Guillaume Brunerie

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 03:09:41