映射类型中判别式联合与交叉类型为何未收窄类型反而得全联合?
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
相关产品推荐
相关产品推荐

