When using type narrowing with TypeScript, never functions do not react the same way every time.
const getValue = () => {
const value = process.env.VARIABLE
if (!value) process.exit(1)
return value
}
In the example below, getValue inferred type is () => string, because value is typed string | undefined, and TypeScript is able to identify that the remaining code after process.exit is unreachable, and the return value is always a string.
const exit = () => {
return process.exit(1)
}
const getValue = () => {
const value = process.env.VARIABLE
if (!value) exit()
return value
}
In this second example, exit is typed () => never, and getValue inferred type is () => string | undefined, because TypeScript is unable to detect that the rest of the function is unreachable if value does not exists. However, if I add a return before the exit call (so return exit()), it works as desired.
Why does it works like this? Is there a way to make it work in the second case?
TypeScript's support for functions that return
never, as implemented in microsoft/TypeScript#32695, only works if the function type is explicitly annotated to returnnever. It would be nice if the type checker could just infer that, but allowing that would make control flow analysis much harder to implement so that it performs well. As it says in the above-liked PR, "this particular rule exists so that control flow analysis of potential assertion calls doesn't circularly trigger further analysis".So, to get the behavior you're looking for, you should annotate
exitas being of type() => never, like this:and then it just works:
Playground link to code