Предположим, у нас есть эта функция:
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>
- возврат функций в любом аспекте анализа потока управления. Это также может быть ошибкой, но я скорее думаю, что мне здесь не хватает некоторых деталей.