Почему Promise <never>не учитывается при анализе достижимости? - PullRequest
5 голосов
/ 06 ноября 2019

Предположим, у нас есть эта функция:

function returnNever(): never {
    throw new Error();
}

При создании IIF код, следующий за ним, становится помеченным как недоступный:

(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 делается синхронным (путем удаления всех await с и async счто касается logError), ошибки нет. Кроме того, если let b: string изменяется на let b: string | undefined, undefined не удаляется после блока try-catch.

Похоже, что есть причина не учитывать await s Promise<never> - возврат функций в любом аспекте анализа потока управления. Это также может быть ошибкой, но я скорее думаю, что мне здесь не хватает некоторых деталей.

...