Skip to content

Type of object property isn’t narrowed when captured by closure #51490

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
leafac opened this issue Nov 11, 2022 · 4 comments
Closed

Type of object property isn’t narrowed when captured by closure #51490

leafac opened this issue Nov 11, 2022 · 4 comments

Comments

@leafac
Copy link

leafac commented Nov 11, 2022

Bug Report

🔎 Search Terms

“TypeScript narrowing of object field captured in closure”

I found #35124 and #9998 but they aren’t quite the same thing.

In my case, it’s an issue that only occurs with object properties, not bare variables like in #35124.

Also, my version is relying on return to avoid creating the closure that would have captured the variable in the first place.

🕗 Version & Regression Information

  • This is a crash
  • This changed between versions ______ and _______
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about “narrowing”
  • I was unable to test this on prior versions because _______

⏯ Playground Link

Playground link with relevant code

💻 Code

(argument: { field: "a" | "b" }) => {
    if (argument.field === "a") return;
    argument.field; // The type of ‘argument.field’ is just ‘"b"’ at this point, because of ‘return’ above.
    () => {
        argument.field; // The type of ‘argument.field’ is back to being ‘"a" | "b"’. I suppose this shouldn’t happen...
    }
};

(argument: "a" | "b") => {
    if (argument === "a") return;
    argument; // Same as above, the type of ‘argument’ is just ‘"b"’ at this point, because of ‘return’ above.
    () => {
        argument; // And here the type of ‘argument’ continues to be ‘"b"’, as I think it should.
    }
};

🙁 Actual behavior

Type of object property isn’t narrowed when captured by closure, like a bare variable is.

The only workaround I could find is the check for the condition again in the closure:

(argument: { field: "a" | "b" }) => {
    if (argument.field === "a") return;
    argument.field; // The type of ‘argument.field’ is just ‘"b"’ at this point, because of ‘return’ above.
    () => {
        if (argument.field === "a") return; // Naturally, this repeated check makes narrowing work.
        argument.field;
    }
};

🙂 Expected behavior

Type of object property should be narrowed when captured by closure, like a bare variable is.

@fatcerberus
Copy link

This is indeed a duplicate of #9998; narrowing doesn’t propagate across function boundaries. I think it only works for argument because TS does some additional control-flow analysis for function parameters to see if they are ever modified that it doesn’t do for regular variables and object properties.

@leafac
Copy link
Author

leafac commented Nov 11, 2022

Hi @fatcerberus,

First, thanks for the quick reply.

This is indeed a duplicate of #9998; narrowing doesn’t propagate across function boundaries.

Fair point. Could the fact that the function wouldn’t even come into existence (because of the return) affect this? At the point of the function definition the type has been narrowed, and it can’t be widened again…

I think it only works for argument because TS does some additional control-flow analysis for function parameters to see if they are ever modified that it doesn’t do for regular variables and object properties.

Can this control-flow analysis be adapted to work with objects, or is there some underlying limitation that would prevent that?

Thanks.

@MartinJohns
Copy link
Contributor

Both points are discussed in #9998, and the TypeScript team is aware of this limitation.

@leafac
Copy link
Author

leafac commented Nov 11, 2022

@MartinJohns Sounds good. Thanks for your answer.

@leafac leafac closed this as completed Nov 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants