如果Typescript 的排版系统是完全正确的,那么无论何时X extends Y
,union X | Y
应该减少到Y
,也就是众所周知的subtype collapsing.(同样,intersection X & Y
应该减少到X
.这些都是absorption laws的表格.)
根据特定的X
和Y
类型,这does发生在Typescript 中.具体地说,基元类型大多是这样的(例如,"abc" | string
减少到string
;123 & number
减少到123
).但TypeScrip的打字系统并不完全正确(也不打算完全正确,参见TS Design Non-Goal #3),而且在某些情况下,方便胜过正确.
关于全面实施吸收定律的旧建议,请参见microsoft/TypeScript#16386,该定律最终作为部分固定和部分拒绝而关闭.
像A | B
这样的对象类型往往不会减少,因为更具体的类型往往具有在吸收后会完全忘记的特性.(对于B
,这是bar
属性.)至少,这对于文档来说不是很好.即使在完全健全的类型系统中,人们也可能希望在他们的文档中使用see A | B
,而不是A
.
此外,一些方便但在技术上不正确的类型脚本构造do关心这些属性,因此在A
和A | B
之间存在明显的差异.
其中一个这样的构造是using the in
operator to distinguish between union members.对于A | B
,您可以使用"bar"
作为关键字,以将范围缩小到B
:
function f(obj: A | B) {
if ("bar" in obj) {
obj.bar.toUpperCase() // okay
}
}
但只要花A
英镑,你就不能:
function f2(obj: A) {
if ("bar" in obj) {
obj.bar.toUpperCase() // error
}
}
因此,A | B
保留了编译器用来执行控制流分析的信息.当然,这非常有用,但在技术上是不正确的,因为它不安全:
class Z extends A {
public bar: number = 1
}
f(new Z()); // <-- accepted but explodes
但是,as mentioned in microsoft/TypeScript#15256, the pull request implementing in
operator narrowing,效用超过了不正确:
现实情况是,大多数unions 已经正确地脱节,没有足够的混叠来体现这个问题.编写in
测试的人不会写出"更好"的判断;实际发生的情况是人们添加类型断言或将代码移到同样不可靠的用户定义类型谓词中.
另一方面,这是另一个这样的 struct :excess property checking.当您将值object literal赋给一个不期望特定属性存在的位置时,您将收到一个错误,警告您编译器将完全忘记该属性,您可能犯了一个错误:
interface C { c: 1 }
const c: C = { c: 1, d: 2 }; // error
// ----------------> ~
// Object literal may only specify known properties,
// and 'd' does not exist in type 'C'.
这很方便,大多数人都想要这个错误,但从技术上讲,这是一个错误的警告,因为{c: 1, d: 2}
、is可以赋给C
.多余的属性判断将incompleteness引入到不需要存在的语言中.
因此,以下是unions 可能会有所帮助的地方:
interface D extends C { d: 2 }
const cd: C | D = { c: 1, d: 2 }; // okay
类型C | D
不会减少到C
,因此看不到{c: 1, d: 2}
具有任何过剩属性.这是in
运算符缩小的另一面,因为d
不是多余属性的原因是因为您可以通过in
运算符缩小来恢复其类型.
多余的属性判断与in
运算符缩小本质上类似于另一种类型系统,其中类型脚本对象类型被关闭/密封/exact(如microsoft/TypeScript#12936中所要求的).在那个世界里,类型将prohibit任何额外的属性.因此B extends A
和D extends C
将仅意味着类/接口层次 struct ,而不是type层次 struct .意思X extends Y
不再意味着X
是Y
的一个子类型,因此X | Y
不再等同于Y
.因此,在某种意义上,对象联合的子类型减少的缺乏是为了允许部分类型脚本生活在精确类型的另一个世界中.
Playground link to code个