在下面的例子中:
操场上联系
type Foo = () => void;
let a: Foo | undefined = undefined;
const c = () => { a = () => console.log("Hi!") };
c();
if (a) {
a();
}
在第4行,a
的类型是let a: Foo | undefined
,但在第8行,它是let a: never
。因此,tsc
显示了这个错误:Type 'never' has no call signatures
在第8行。
但是如果第2行是let a: Foo | undefined;
(去掉变量赋值)
我猜是和类型推断有关的东西。文档声明:
在TypeScript中,有几个地方使用了类型推断在没有显式类型注释时提供类型信息。
let x = 3; ^ let x: number
但是在这个例子中有一个显式的类型注释
我理解TS无法知道函数c
是否将被执行,因此,a
将被分配。这就是为什么我把a
类型的显式定义放在它的声明中。
那么为什么在第8行不尊重为a
定义的类型?
这是不合理的行为,正如你在你的问题中所指出的。该行为在GitHub问题跟踪器上的一个公开问题中被注意到,题为"控制流分析中的权衡":
乐观:本地行为不良
TypeScript编译器有这样的代码:enum Token { Alpha, Beta, Gamma } let token = Token.Alpha; function nextToken() { token = Token.Beta; } function maybeNextToken() { if (... something ...) { nextToken(); } } function doSomething() { if (token !== Token.Alpha) { maybeNextToken(); } // is this possible? if (token === Token.Alpha) { // something happens } }
乐观地假设
token
没有被maybeNextToken
修改,错误地将token === Token.Alpha
标记为不可能。然而,在其他情况下,这是一个很好的检查!参见后面的示例。
问题是,在其他情况下,使编译器更悲观将是有害的,因为编译器将无法检测到代码中的真正错误。在写这个答案的时候,这个问题已经超过5年了,仍然没有解决,看起来语言设计者们还没有就是否需要做些什么达成一致,或者如果需要做些什么。