-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Type guard affects type of variable in surprising way #50916
Comments
This is an effect of #50044. The issue here is that the argument type and the asserted type are subtypes of each other, and therefore appear interchangeable in control flow analysis. From the PR:
So this is effectively a design limitation, but we'll continue to think of ways in which to improve it. |
Would it be possible to have TS more eagerly resolve |
The core issue is that type Identity<T> = {[K in keyof T]: T[K]} & { __tag__: void }; Fundamentally, when the argument and asserted types are subtypes of each other, the reason we favor the asserted type is that you must have written the assertion for some reason. I'm not sure what the reason is in your example, but presumably there is some difference in behavior? |
Here's an example that is a lot closer (though still simplified) to the actual situation that we had: |
I that last example, I'd recommend re-writing the export type Require<T, K extends keyof T> =
Pick<T, K> extends Required<Pick<T, K>> ? T : Omit<T, K> & Required<Pick<T, K>>; |
That's what we pretty much ended up doing (the playground link in the description actually has an example at the bottom of the code) and we added this optimization to our other utility types, but it was certainly an unexpected footgun as this was the first time we've ever had to optimize our types in this particular way. |
Agreed about the footgun. We'll continue to think about ways to improve this. |
Thanks @ahejlsberg ! |
Bug Report
π Search Terms
type guard / fall through / narrow / change / lazy / evaluate
π Version & Regression Information
β― Playground Link
Playground link with relevant code
π» Code
π Actual behavior
The type of
x
is "narrowed" toIdentity<Identity<Identity<Identity<Identity<Identity<Identity<Identity<{a: number;}>>>>>>>> | Identity<Identity<Identity<Identity<Identity<Identity<Identity<Identity<{b: number;}>>>>>>>> | Identity<Identity<Identity<Identity<Identity<Identity<Identity<Identity<{c: number;}>>>>>>>>
It's as if a variable gets narrowed to the union of the types of both sides of the type predicate, e.g.
π Expected behavior
The type of
x
doesn't change.The text was updated successfully, but these errors were encountered: