-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Fix discriminant property check #29110
Conversation
Based on your comment and the repro, isn't the real issue that we don't combine discriminant narrowing with other generated facts in control flow well? |
@weswigham Well, yes and no. We would do better if we didn't have the design limitation that causes us to reset narrowed types for properties when the shape of an object changes because of a discriminant check, and we might in fact not have this issue. But it would require the ability to "record and replay" preceding narrowing checks, and that would definitely complicate the control flow analyzer. And, either way, there's really no point in considering |
In the given example, sure, but I can't help but feel that someone somewhere has taken a dependency on a property of |
@ahejlsberg When you say:
What does shape mean: changing the apparent properties aggregated across the union type, or something else? Would it be too difficult (or wrong) to test for this change of shape, rather than classifying |
@weswigham Discriminants are meant to be unit types and we only narrow based on equality and truthiness checks. With a discriminant of |
A |
Also 2-3 months != 2 weeks. This has been out for awhile (and has shipped in stable versions of TS). |
@weswigham No, we don't do discriminant based narrowing for type Foo = { a: string, x: number } | { a: 0, y: number };
function xxx(obj: Foo) {
if (typeof obj.a === "string") {
obj.x; // Error (because we don't narrow here)
}
if (obj.a === 0) {
obj.y; // Ok (discriminant based narrowing)
}
} With the fix in this PR our behavior changes in the following because we no longer consider type Foo = { a: string | undefined, x: number } | { a: string, y: number };
function xxx(obj: Foo) {
if (obj.a === undefined) {
obj.x; // Previously would narrow, now doesn't
}
} As I said, I can't imagine anyone having taken a dependency on this in the last few weeks (or months or whatever). This PR is the right fix for the issue given the design constraints we currently have. |
If I understand the issue properly it seems to be a tradeoff between
The number of people affected by (1) seems likely to be larger than (2), and (2) can be fixed by replacing the equality test with a type guard. |
@jack-williams Yes, that's the tradeoff. But #2 is actually even further qualified: Breaking code for early adopters of discriminant narrowing involving properties that have no single underlying property of a pure unit type or union of unit types. |
In #27695 we were slightly too permissive in what we consider a discriminant property. Specifically, we required the combined union type to contain at least one unit type, but that unit type could have occurred in combination with non-unit types. We now check that at least one underlying property has a pure unit type or union of unit types.
Fixes #29106.