TypeScript中两种联合类型实现方式的差异是什么?
TypeScript类型X与Y的区别分析
类型定义示例
// 类型X:单一对象类型,属性a为联合值 type X = { a: "hi" | "bye"; b: number; } // 类型Y:两个对象类型的联合 type Y = { a: "hi", b: number } | { a: "bye", b: number }
实现方式回顾
类型X的生成方式
通过MergeUnion工具类型将联合类型合并为单一对象:
type A = { a: "hi", b: number } type B = { a: "bye", b: number } type C = A | B type MergeUnion<T> = { [K in ({} & keyof T)]: T[K] }; type X = MergeUnion<C>
类型Y的生成方式
直接取两个对象类型的联合(注:原代码中a|b应为笔误,实际是A|B):
type Y = A | B;
核心差异点
1. 类型本质不同
- X是单一对象类型:代表一个同时拥有
a和b属性的统一结构,a的取值范围是"hi"或"bye",b必须是number类型,没有分支区分。 - Y是对象类型的联合:代表两种独立的对象结构之一——要么是
{a: "hi", b: number},要么是{a: "bye", b: number},类型本身是两个结构的集合。
2. 类型检查与窄化行为不同
- 使用X类型时,TypeScript不会对
a的具体值做额外窄化,可自由修改a在联合范围内的值:const x: X = { a: "hi", b: 1 }; x.a = "bye"; // 完全合法,符合X的类型定义 - 使用Y类型时,TypeScript会根据初始赋值自动窄化类型,后续修改超出窄化范围会触发错误:
若要修改,需通过类型断言绕过窄化:const y: Y = { a: "hi", b: 1 }; // 此时y被窄化为{ a: "hi", b: number }类型 y.a = "bye"; // 类型错误:不能将类型"bye"分配给类型"hi"(y as Y).a = "bye"; // 合法
3. 类型操作的表现差异
当对两种类型进行工具类型操作时,结果可能不同:
- 本示例中
X['a']和Y['a']都会得到"hi" | "bye",表现一致;但如果涉及可选属性、索引签名或条件类型时,差异会更明显。比如若其中一个子类型添加额外属性,合并后的X会包含该属性的联合类型,而Y会保留分支结构。
兼容性
在当前示例的结构下,X和Y是双向兼容的:
- X类型的对象可以赋值给Y类型,因为X的每个可能值都符合Y的其中一个分支。
- Y类型的对象也可以赋值给X类型,因为Y的每个分支都完全匹配X的结构要求。
但这种兼容性仅针对当前结构,若类型结构发生变化(比如某分支添加可选属性),兼容性可能被打破。
内容的提问来源于stack exchange,提问作者Islam Y-
相关产品推荐
相关产品推荐

