I wrote a function that returns a function that solves world hunger, but only if anyone is still hungry, otherwise null.
If and only if anyone is still hungry, the inner function is created, and encloses the const variable about whether anyone is still hungry.
During the execution of this function, we know logically that the variable has been narrowed to either true
or "fileNotFound
.
And issue #56908 by @ahejlsberg admits it should work:
We currently preserve type refinements in closures for const variables
But TypeScript complains.
The playground shows the types:
type IsStillHungry = true | false | 'fileNotFound' | undefined
function solveWorldHungerStrategy(opts: { isAnyoneStillHungry: IsStillHungry }) {
const condition = opts.isAnyoneStillHungry
if (condition) {
condition
// ^? const condition: true | "fileNotFound"
function pretendToSolveWorldHunger() {
condition
// ^? const condition: boolean | "fileNotFound" | undefined
}
return pretendToSolveWorldHunger
}
return null
}
TypeScript never maintains narrowing of closed-over variables inside the body of function
declarations (statements), which are hoisted to the top of the enclosing function or global scope. In general that means the function can be called before the narrowing takes place. Indeed, inside the same pull request you quoted, microsoft/TypeScript#56908, it says:
Type refinements are not preserved in inner function and class declarations (due to hoisting).
If you change the declaration pretendToSolveWorldHunger
from a statement to an expression, then the narrowing is preserved:
function solveWorldHungerStrategy(
opts: { isAnyoneStillHungry: boolean | 'fileNotFound' | undefined }
) {
const condition = opts.isAnyoneStillHungry
if (condition) {
condition
// ^? const condition: true | "fileNotFound"
const pretendToSolveWorldHunger = function () {
condition
// ^? const condition: true | "fileNotFound"
}
return pretendToSolveWorldHunger
}
return null
}
Of course a const
that holds a primitive value cannot be reassigned, so in theory TypeScript could decide to preserve any narrowings of such const
variables, regardless of hoisting. But this is a missing feature of TypeScript, as described in the open feature request microsoft/TypeScript#36913.