TypeScript 2.0控制流分析,永不输入

时间:2016-09-24 01:30:13

标签: typescript typescript2.0

我在TypeScript 2.0中有这个功能:

function test(a:string) {
    var b = typeof a === "function" ? [a] : a;
}

预期的行为: b的类型为string。可以针对始终 false的条件发出警告。

实际行为: b的类型为never[] | string

为什么?

1 个答案:

答案 0 :(得分:1)

刚刚玩了这个,并且考虑到它,实际上TS编译器非常聪明。 TSC非常了解typeof运算符并在流类型分析中跟踪它。

关于TS的一个非常酷的事情是,而不是强加一个高级的,高级牧师,类似于OO(认为多级多层次的层次结构),逆变和协变类型(想想Scala {{1在你身上,TSC只是试图根据它们的美丽混乱来模拟JS类型,并且用尽可能简单的机制来做到这一点。

TS的类型系统在某种程度上是一种金属类型系统:不是太多而不是太少;恰到好处。

所以,与那个序言一起,让我们分析一下你的问题“为什么会这样?”来回答是什么。

三元表达式[+T]的类型是[-T]condition ? X : Y的并集。这是因为,在一般情况下,当条件为真时返回X&它的类型为typeof(X),否则返回Y&它的类型是typeof(Y),所以它必须是一个或另一个。每个JS程序员都在她/他的头脑中进行这种类型的流分析,因为vanilla JS没有任何正式的语法来编写类型。但TypeScript,FaceBook的Flow&其他compile-to-JS系统将类型语法带到JS表中,所以现在我们可以将三元构造的类型识别为typeof(X)

在您的示例中,X是[a],Y是字符串,因此编译器试图找出的类型是typeof([a])| typeof运算(a)中。 RHS很简单:它只是typeof(Y),因为函数的论证是这样说的。 LHS类型必须是一个数组类型,因为typeof(X) | typeof(Y)这样说,因此它将以TS类型语法写成X []。如果只是我们可以弄清楚X是什么。

对于LHS,编译器首先推断string必须是Function,因为条件是这样的。但该函数的签名表明[a]typeof(a)。因此,在typeof(a)的上下文中,string必须同时为[a]以及typeof(a)。这听起来有点量子(想想量子态),但它并不是真正致盲的科学。它是类型的简单连接或交集(类型AND运算符的大字),并且可以写为(Function& string)。所以现在这个三元表达式的整体类型是Function

最后,编译器知道所有Function值的集合和所有字符串值的集合的交集是空集。因此,TSC进一步将string减少到(Function & string)[] | string,因为该值可能“永远不会发生”,这就是为什么在这个非常冗长的答案中,TSC表示类型为Function & string

然而,一组nevers永远不会发生,所以这是另一组空值。这将我们带到您预期的行为,即b的类型只是never,这是never[] | string的一个简单子集(如同一组)。编译器可以计算这个额外的步骤并进一步减少类型表达式。这可能是一个功能或一个错误,取决于你是否认为简单是一个功能或其他!

与Nitzan的答案一致,string永远不会发生永远不会发生的事情,因此这些类型代表同一组价值观。 never[] | string可能更准确一些,因为它告诉你如果确实要返回任何内容,可能会返回的形状(它是一个容器)。

另一方面,你的函数不会抛出异常,所以说类型可以减少到never[],因为Nitzan认为这可能是错误的或误导性的。 never[]是三元表达式的类型:

never | string

我喜欢这些小谜题,并经常想知道一篮子空苹果是否与一篮子空橙子相同。

干杯。