假设我们有这样的功能:
function returnNever(): never {
throw new Error();
}当创建生命时,它之后出现的代码被标记为不可访问的代码:
(async () => {
let b: string;
let a0 = returnNever();
b = ""; // Unreachable
b.toUpperCase(); // Unreachable
})();这如预期的那样起作用。注意,a0被推断为never类型。
但是,如果returnNever()返回一个Promise<never>并得到等待,则行为是不同的:
(async () => {
let b: string;
let a1 = await Promise.reject(); // returns Promise<never>
b = ""; // Not unreachable?
b.toUpperCase(); // Not unreachable?
})();在这种情况下,a1也被推断为never类型。但是之后的代码并没有被标记为不可访问。为什么?
背景:我最近偶然发现了一些类似于以下代码的logError函数。它在catch块中使用。通过这种方式,我发现,不是可达性分析,而是明确的分配分析受以下因素的影响:
declare function fetchB(): Promise<string>;
async function logError(err: any): Promise<never> {
await fetch("/foo/...");
throw new Error(err);
}
(async () => {
let b: string;
try {
b = await fetchB(); // Promise<string>
} catch (err) {
await logError(err); // awaiting Promise<never>
}
b.toUpperCase(); // Error: "b" is used before assignment
})();如果logError是同步的(通过删除与logError相关的所有await和asyncs ),则不会出现错误。另外,如果将let b: string更改为let b: string | undefined,则在try-catch块之后不会删除undefined。
在控制流分析的任何方面,似乎都有理由不考虑await的Promise<never>-returning函数。这也可能是一个bug,但我更愿意在这里忽略一些细节。
发布于 2022-10-14 02:37:02
从技术上讲,承诺是异步进程,这意味着在对承诺本身进行评估时,承诺之后的代码将继续运行。因此,即使承诺最终因错误而终止,仍然有可能在仍在对其进行评估时达到它之后的代码。这可能是为什么代码没有标记为不可访问的原因,尽管在现实中,将后面的任何代码视为不可访问的代码是有意义的。
https://stackoverflow.com/questions/58732814
复制相似问题