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 return never. 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 exit as being of type () => never, like this:
const exit: () => never = () => {
return process.exit(1)
}
and then it just works:
const getValue = () => {
const value = process.env.VARIABLE
if (!value) exit()
return value
}
// const getValue: () => string
Playground link to code
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With