TypeScript 4.7联合类型重叠成员冲突校验逻辑差异咨询
TypeScript 4.7 联合类型属性校验差异说明
// 1 case - conflict number VS boolean type A2 = { x: number; y: number; }; type B2 = { y: boolean; // conflict z: number; }; type U2 = A2 | B2; let u2: U2; u2 = { x: 1, y: 1 }; u2 = { y: false, z: 5 }; u2 = { x: 1, y: false, z: 3 }; // error u2 = { x: 1, y: 2, z: 3 }; // error // 2 case - conflict number VS string type A = { x: number; y: number; }; type B = { y: string; // conflict z: number; }; type U = A | B; let u: U; u = { x: 1, y: 2 }; u = { y: "2", z: 3 }; u = { x: 1, y: 2, z: 3 }; // ok. why? u = { x: 1, y: "2", z: 3 }; // ok. why?
核心规则前提
- 结构化类型系统本身允许多余属性:将已声明的变量赋值给目标类型时,只要变量包含目标类型的全部必填属性、且对应属性类型兼容,即使存在额外属性也不会报错。
- 多余属性检查是TypeScript针对对象字面量直接赋值场景加的额外语法校验,不属于结构化类型的基础规则,仅在特定条件下触发。
两个场景校验逻辑差异的根本原因
多余属性检查在联合类型赋值场景的触发逻辑如下:
- 遍历联合类型的所有成员,根据重叠属性的类型兼容性,筛除存在明确类型冲突的候选成员
- 若最终仅剩余1个候选成员,对该成员做精确匹配校验,存在多余属性直接报错
- 若最终剩余2个及以上候选成员,不触发多余属性检查,仅按基础结构化规则校验兼容性,只要对象满足任意一个成员的结构要求即可通过
场景1(number 对比 boolean)触发报错的逻辑
number和boolean属于被TypeScript明确判定为完全无交集的原始类型:
- 当赋值对象的
y为number类型值时,会直接筛除B2候选,仅剩A2,校验时发现对象存在A2中没有的z属性,触发多余属性报错 - 当赋值对象的
y为boolean类型值时,会直接筛除A2候选,仅剩B2,校验时发现对象存在B2中没有的x属性,触发多余属性报错
场景2(number 对比 string)校验通过的逻辑
number和string在联合候选筛选阶段,不会被判定为明确冲突:
这是TypeScript从早期版本遗留的设计规则:为了兼容JavaScript中对象数字键自动转字符串的语言特性,类型系统在联合候选匹配的弱判定逻辑中,保留了number和string的兼容性豁免,不会因为重叠属性是number/string组合就筛除对应的候选成员。
因此当对象同时携带x、z属性时,A和B会同时保留在候选列表中,不会触发单成员的多余属性检查,仅按基础结构化规则校验:
- 当
y为number值时,对象满足A的全部结构要求,校验通过 - 当
y为string值时,对象满足B的全部结构要求,校验通过
注:如果直接将同结构的对象字面量赋值给单一类型
A或B,不存在联合候选筛选逻辑,会直接做属性类型校验,比如let b: B = {y:2, z:3}会明确报number不能赋值给string的错误。
规则来源说明
该差异属于TypeScript的已知设计行为,核心判定逻辑在多次版本迭代的PR中逐步固化,官方文档在「多余属性检查」章节仅明确了联合类型下多余属性检查的单成员触发条件,未单独标注number与string的特殊兼容判定,该规则是社区通过大量测试用例和源码阅读总结的既定实现。
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

