为什么 TypeScript 不允许读取闭包中的未定义变量?



我只是偶然发现了这个细节,在文档中找不到任何东西。以下是一些有效的TypeScript代码,它输出undefined:

let x: number;
const f= () => {
const y= x;
console.log(y);
}
f();

游乐场

TypeScript假定(在f内部(x被分配了一个值。y的类型是number,而不是number | undefined。这是什么原因?

我查了一下关于不和谐的文件,找不到解释。

这里的一般问题在microsoft/TypeScript#9998中突出显示。

编译器执行所谓的控制流分析,试图跟踪代码中不同位置的变量类型。正是这个通用功能使编译器能够捕获指定错误之前使用的错误:

let oops: string;
oops.toUpperCase(); // error, used before assigned
let okay: string;
okay = "assigned";
okay.toUpperCase(); // okay

这种控制流分析是有用的,但远非完美。编译器并非无所不知;它并没有模拟程序运行的所有可能方式。它也不具备人类开发者的推理能力。相反,它使用启发式,一组简化的假设,使类型检查在编译过程中易于处理。

当控制流跨越函数边界时,这种限制很容易观察到。编译器实际跟随控制流进出函数是不可行的,就好像函数体是内联的一样。相反,它会做出假设,这可能会导致误报(良性代码的编译器错误(和误报(错误代码没有编译器错误(。这是microsoft/TypeScript#9998的主题。


当引入此控制流分析功能时,编译器倾向于警告外部范围中的变量未初始化。这会产生您正在查找的错误。

不幸的是,在外部变量真的被初始化的情况下,它也会发出警告,但编译器没有注意到。误报太多,并提交了一个错误:microsoft/TypeScript#9757。

修复是一个简单的假设变化:正如在microsoft/Typescript#10815中所提到的,编译器将";假设外部变量总是在控制流分析中初始化";。

这消除了一类误报,但引入了一类漏报,例如代码示例中的误报。


有人(你?(可能会提出一个新问题,要求对此进行改进。也许有人可能会建议,在整个代码库中从未分配变量的情况下,编译器可以恢复到microsoft/TypeScript#10815之前的行为。也就是说;假设一个外部变量被赋值,当且仅当程序中某个地方至少有一个对该变量的赋值";。

但我对这样一个建议被采纳的可能性并不乐观。在检查变量初始化时执行的每一个额外的分析位都具有相应的性能成本位。除非在修复真实世界TypeScript代码的问题方面付出额外的努力,否则这将不值得做。。。即使它严格地提高了类型检查器的可靠性和完整性。

因此,问题是:这个问题有多普遍?我对此没有明确的答案,但我预计这并不常见。。。我一直找不到有人报告这种确切情况的例子。

在您的情况下,外部变量永远不会被赋值。我确实看到有人报告说,在赋值之前使用了外部变量,而编译器没有捕捉到它。即使在这种明显更常见的情况下,他们似乎也不打算对此采取任何措施:请参阅microsoft/TypeScript#42036。

最新更新