Skip to content

Refinement in async functionΒ #61365

Closed
Closed

Description

πŸ”Ž Search Terms

"refinement async function"

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ. No entries matching "refinement" and nothing matching "async" or "await"

⏯ Playground Link

https://www.typescriptlang.org/play/?target=99&ts=3.8.3#code/IYZwngdgxgBAZgV2gFwJYHsIwEbAE4AUAlDAN4CwAUDDTHgKbIJ5YT0DuMACnugLaoQ9AgGsYAXgB8MIcgAqqPvXQJkogDQwAjAAYdRIgG4qAXypVQkWIhQYscdOmJkqtGABtGMZPRDIAXDAQCO7uMAA+ZDAAFvSh6IEA5OzoeO4AJokwJhJBIe7G1LRQmCDongB07ugA5gSJ0agAhIlE5kU0sgpKKmrOUi4dbt6+yLmkMXHVSSlpmWZDJm2UrrTA7MCoY7iERqs0JRBlldV1iT5+APyJmhfIy8MA9I90SBCoEDXesTAADrw1PDAPgyaLodggb7AMZ3GCCGAAAwmsXiM1SGSyJgROFUMDkYF+9AAylA8KhfmMPiU8AwoMh3GAYPsaM86PQ4B9fHCYeg8qFNEJ6AJPgy4TUIKkPl9kNFod96AxEpDgFh1psxuxGlBojA+MBGcB4pwZfQYKAQKhxUoIDyYsBfoT7Kg8H5mXdLhUUdVCgsqA4nHsVpQgA

πŸ’» Code

async function bar() {
    return new Promise(k => setTimeout(k, 100));
}

async function foo() {
    let test: null | { hello: 'world' } = null;
    console.log('hi!')

    setTimeout(() => {
        test = { hello: 'world'}
    })

    await bar();
    console.log('test?', test)
     // running the program shows that test is `{ hello: 'world' }` but TypeScript incorrectly 
     // refines it to null, seemingly ignoring that there's an await which may allow the assignment to happen first
    test?.hello;
}

foo();

πŸ™ Actual behavior

Getting a type error, where TypeScript refined the type of test to null

πŸ™‚ Expected behavior

The inferred type of test should be null | { hello: 'world' }

Additional information about the issue

There seems to be an issue where TypeScript ignores awaits (and yield/yield*) when doing refinement. It seems to me that at any point after an await you can no longer assume a refinement holds true.

I understand perhaps this may be a necessary tradeoff to avoid false negatives in many cases. However since there is an explicit assignment to a let declared variable in the same function it seems a bit funny. If this is a known limitation perhaps an entry in the FAQ could be added.

Activity

  1. MartinJohns commented on Mar 6, 2025

    @MartinJohns
    Contributor
  2. philipnilsson commented on Mar 6, 2025

    @philipnilsson
    Author

    Martin Johns (@MartinJohns) It's not obvious to me that the same is true for an await based on the above template.

    An await could be though of as desugaring into promise.then(callback), where callback is the rest of the computation, where if applying your rule of "narrowings are not respected in callbacks" you'd expect the narrowing to no longer be present, exactly the opposite of what you claim, no?

    Anyway, I wasn't able to find the precedent here so if this still counts as a duplicate I can close and it's up to you if you think it warrants adding to the FAQ / template.

  3. philipnilsson commented on Mar 6, 2025

    @philipnilsson
    Author

    Since there seems to be discussion here #9998 (comment) I'll just close though.

  4. locked as resolved and limited conversation to collaborators on Oct 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

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