0

我在 TypeScript 2.0 中有这个功能:

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

预期行为:类型bstring对于始终为假的条件,可能会发出警告。

实际行为:类型bnever[] | string

这是为什么?

4

1 回答 1

1

刚刚玩过这个,想一想,实际上 TS 编译器非常聪明。TSC 非常了解 typeof 运算符并在流类型分析中对其进行跟踪。

关于 TS 真正很酷的事情之一是,它不是强加一个高雅、高级、类似 OO(想想多层次的类层次结构)、逆变和协变类型(想想 Scala [+T][-T]聪明但复杂)你,TSC 只是尝试根据 JS 类型的美丽混乱来建模它们,并且它使用尽可能简单的机制来做到这一点。

TS 的类型系统在某种程度上是一个金发姑娘类型系统:不多也不少;正好。

所以,随着那个序言,让我们分析一下在回答你的问题“为什么会这样?”时发生了什么。

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

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

对于 LHS,编译器首先推断typeof(a)必须是 Function,因为条件是这样的。但是函数的签名说typeof(a)string. 因此,在 的上下文中[a]typeof(a)必须同时是Function以及string。这听起来有点量子(想想量子态),但它并不是真正令人眼花缭乱的科学。它是类型的简单合取或交集(类型与运算符的大词),可以写为(函数和字符串)。所以现在这个三元表达式的整体类型是(Function & string)[] | string.

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

然而,一个从不数组永远不会发生也是事实,所以这是另一组空值。这给我们带来了您预期的行为,即 b 的类型只是string的一个微不足道的子集(如在相同的集合中)never[] | string。编译器可以计算出这个额外的步骤并进一步减少类型表达式。这取决于您是否认为简单性是一个特性或其他特性,这要么是一个特性,要么是一个错误!

与 Nitzan 的回答一致,never[]永远不会发生,因为永远不会发生,所以这些类型代表相同的一组值。never[]可能更精确一点,因为它告诉您如果确实要返回任何东西,可能会返回的形状(它是一个容器)。

另一方面,您的函数永远不会抛出异常,因此说可以将类型简化never | string为 Nitzan 建议的可能是错误的或具有误导性的。never | string是您将为三元表达式获得的类型:

condition ? throw Error("WTF") : "Ecma Scriptus MMXV"

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

干杯。

于 2016-09-24T12:53:02.817 回答