Skip to content

Type narrowing not preserved in closure between narrowing and usage before last assignment (incomplete "Preserved Narrowing in Closures Following Last Assignments") #57405

Closed as not planned

Description

🔎 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'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

https://www.typescriptlang.org/play?ts=5.4.0-dev.20240214#code/GYVwdgxgLglg9mABCAzgUwHIgLYCM0BOAFAB4BciYO+BAlIgN4BQiiBaUIBSUBIaTAL5MmoSLASIoaFFADqcAgGsYYAOZFgcOLgCGBClTyFEAH2RgAJmmCq0l+s1YAbDoggALNBCX2AYtp6BpTUJubg1rZg9izuCLKIwEgAvIhE9MkAfIyxrBDxUIgwKFjGBIipnt6+lgE6+ogAhMmpETZ2lrlxYAlQ2AAOFUUloeUAZGPI6KU0RFU+-oH6tCKsrAD064gAksBSHsWIznbDcdjYaGDSlohwIFAANPtoiIQEiohqcDKIugDuugAnl1NvManUgkMtPUCKtEMJWOxONxEmAhCIxNB4DwZFAAELvXxgTRLYJGGhmCyRDqOWKuQpgxYwwyjSltKIxPIFVFDdIVbJONb5HqFYozEyVLwLWqkpotKntaKdNbdXoDIZi1kTKaYUZzKXg0krEFbXb7Q7HaKnfLnS7XW73J5QLyvAjvcpfH7-IFdRkymFQ0lwhFsDhcJBJIRAA

💻 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

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions