Closed as not planned
Description
openedon Feb 14, 2024
🔎 Search Terms
- Preserved Narrowing in Closures
- Last Assignments
Read #56908 and https://devblogs.microsoft.com/typescript/announcing-typescript-5-4-beta/#preserved-narrowing-in-closures-following-last-assignments because I feel like this related case should also be handled.
🕗 Version & Regression Information
- I was unable to test this on prior versions because it didn't exist at the time (new for 5.4.0-beta: https://devblogs.microsoft.com/typescript/announcing-typescript-5-4-beta/#preserved-narrowing-in-closures-following-last-assignments).
- This behaviour happens in the latest 5.0.4-beta and nightly.
I'm not sure if this is a bug, intended behaviour or a missing feature. To me, this looks like incomplete type narrowing in closures before the last assignment, when it should be trivial to narrow the type.
⏯ Playground Link
💻 Code
function useNumber(x: number) {
return true
}
function testBroken(foobar: number | undefined) {
let checkedFoobar: number | undefined
const fn = () => {
const isNumber = checkedFoobar !== undefined
const tmp = isNumber && useNumber(checkedFoobar) // error; because `checkedFoobar` is `number | undefined`
// If this line is commented out, the error goes away in 5.4.0
// (Before 5.4.0 the error always exists, even without this assignment)
checkedFoobar = foobar
}
return fn
}
🙁 Actual behavior
In the sample above:
const isNumber = checkedFoobar !== undefined
const tmp = isNumber && useNumber(checkedFoobar) // error; because `checkedFoobar` is `number | undefined`
🙂 Expected behavior
TypeScript should know that checkedFoobar
is number
(and keep that knowledge in the isNumber
variable).
There's no assignment between the check and the usage, so any type narrowing should hold.
In the sample above:
const isNumber = checkedFoobar !== undefined
const tmp = isNumber && useNumber(checkedFoobar) // works; because `checkedFoobar` should be `number`
Additional information about the issue
An ugly workaround is to repeat the undefined
check directly in the condition:
const tmp = checkedFoobar !== undefined && useNumber(checkedFoobar) // works; because `checkedFoobar` is `number`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Metadata
Assignees
Labels
No labels