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

映射类型中判别式联合与交叉类型为何未收窄类型反而得全联合?

TypeScript判别式联合类型映射的类型收窄差异问题

先定义如下判别式联合类型:

enum Kind {
    A = "a",
    B = "b",
    C = "c"
}

type Value =
    | { kind: Kind.A, value: 1 }
    | { kind: Kind.B, value: 2 }
    | { kind: Kind.C, value: 3 }

目标是将其转换为{ a: 1, b: 2, c: 3 }的对象类型,但实现时遇到了类型收窄的差异问题:

直接使用具体字面量类型:收窄正常

当直接用Kind.A这种具体值做交叉类型时,TypeScript能正确收窄到对应成员的value类型:

type t1 = (Value & { kind: Kind.A })["value"]
//   ^? type t1 = 1

使用泛型/映射类型:收窄失效

但在映射类型或通过infer得到的类型参数中,相同逻辑会得到全联合类型:

type t2 = { [K in Kind]: (Value & { kind: K })["value"] }
//   ^? type t2 = { a: 1 | 2 | 3, b: 1 | 2 | 3, c: 1 | 2 | 3 }

type t3 = Kind.A extends infer K extends Kind.A ? (Value & { kind: K })["value"] : never
//   ^? type t3 = 1 | 2 | 3

原因解析

这是TypeScript对具体字面量类型和泛型类型参数的处理逻辑差异导致的:

  • 对于具体字面量类型(如Kind.A),TypeScript会直接在Value联合中找到完全匹配kind: Kind.A的成员,交叉操作后仅保留该成员,因此value类型能精准收窄。
  • 对于泛型参数(包括映射类型中的K in Kind,本质是遍历联合的泛型参数),TypeScript不会针对每个K的具体值做分支收窄,而是将K视为整个Kind联合的代表。此时Value & { kind: K }会保留所有Value成员(因为所有成员的kind都属于Kind),最终value类型就是所有成员的value联合。

简单来说,泛型参数未被具体化时,TypeScript不会对联合类型的单个分支做精细化判断,而是以整个联合的视角处理。

正确实现方式

使用内置工具类型Extract替代交叉操作,它能针对泛型参数正确提取联合类型中符合条件的成员:

type MapValueToKind = {
  [K in Kind]: Extract<Value, { kind: K }>["value"]
}
//   ^? type MapValueToKind = { a: 1, b: 2, c: 3 }

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 17:17:39