为何TypeScript含可选字段的交叉类型会拓宽而非收窄?
这是TypeScript在条件类型中处理两类对象类型时,子类型判断规则不一致导致的:
1. Example1 结果为false的原因
THasOptionalNum是仅包含可选属性的对象类型:{ A?: number }。
在条件类型的严格子类型判断逻辑中,若目标类型仅由可选属性构成,源类型不能包含目标类型未定义的任何属性——哪怕源类型的属性是必填项。ObjectWithOnlyRequiredString拥有B: string这个THasOptionalNum未定义的属性,因此ObjectWithOnlyRequiredString extends THasOptionalNum的结果为false。
2. Example2 结果为true的原因
交叉类型THasOptionalNum & THasRequiredString会被TypeScript合并为{ B: string; A?: number },这个类型包含必填属性B。
当目标类型存在至少一个必填属性时,子类型判断规则会放宽:只要源类型满足所有必填属性的类型要求,源类型可以:
- 缺少目标类型的可选属性
- 拥有目标类型未定义的额外属性
ObjectWithOnlyRequiredString完全满足必填属性B: string的要求,因此ObjectWithOnlyRequiredString extends { B: string; A?: number }的结果为true。
补充说明
这种规则差异的设计意图在于:仅含可选属性的类型被视为「可能为空」的类型,而带必填属性的类型明确要求实例必须包含指定属性,两者的语义边界不同。另外,条件类型的严格子类型判断和日常变量赋值的兼容性规则略有差异——比如非字面量的{ B: string }变量可以赋值给THasOptionalNum类型,但条件类型的extends判断会返回false。
内容的提问来源于stack exchange,提问作者Tomas Reimers

