You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Partially overlapping union discriminators are not assignable from a union of discriminating values #45230
This is the behavior in every version I tried, and I reviewed the FAQ for entries about Type system behavior and Classes
I tried versions from v3.3.3 to v4.3.5, as those were available in the Playground at this time.
Note that between v3.3.3 and v3.5.1 something changed that affects type C from my example below. In v3.3.3 the assignment to const c: C is an error, from v3.5.1 its okay.
// Original Problem:typeA={aprop: 'a'|RegExp}|{aprop: 'b'|RegExp};declareconstaprop: 'a'|'b';consta: A={ aprop };// error// Distillation:typeB={bprop: 1|string}|{bprop: 2|string}declareconstbprop: 1|2;constb: B={ bprop }// same error// error appears only for "infinite" types, this works finetypeC={cprop: 1|3}|{cprop: 2|3}declareconstcprop: 1|2;constc: C={ cprop }// C['cprop'] == 1 | 2 | 3// more detailed exampletypeInfinite=string;typeD={dprop: 1|Infinite}|{dprop: 2|Infinite}typeDprop=D['dprop'];// 1 | 2 | string -> "merging" seems to work correctlydeclareconstdprop: 1|2;constd1: D={dprop: 1}// OKconstd2: D={dprop: 2}// OKconstd3: D={ dprop }// error
🙁 Actual behavior
The assignments to const a: A, const b: B, and const d3: D raise an error.
🙂 Expected behavior
As demonstrated in the assignments to const d1: D and const d2: D, my partially discriminating property dprop can be either of the two discriminating values. So It should be possible to assign to dprop a value that is either of them, as in the assignment to const d3: D.
In general if you have some type { a: U1, b: U2, c: U3, d, U4 }, you can't tell if that's going to be assignable to some target type without doing a computation involving size(U1) * size(U2) * size(U3) * ... different expansions of the type to enumerate all possible inhabitants. So we're always going to sometimes reject some of these assignments because the special-casing to allow the trivial cases has some limits.
That said, I don't see why this should matter when a non-literal type appears in the target.
This is the expected behavior. It is far too expensive to compute this kind of overlapping assignability for all cases, so we only currently do this work when a union has overlapping discriminant properties. A property is a discriminant property when its type consists of only unit types (e.g., 1 or "foo"). In the three failing cases above, the property in question consists of a unit type unioned with a non-unit type (string, RegExp, etc.), which means the property is not considered a discriminant and thus does not qualify for this assignability rule.
In the case of C, both types in the union contain discriminant properties as they consist only of unit types or unions of unit types (1 | 3 and 2 | 3).
Activity
jcalz commentedon Jul 29, 2021
The change was "smarter union type checking", implemented in #30779 and released in TS3.5.
RyanCavanaugh commentedon Jul 29, 2021
In general if you have some type
{ a: U1, b: U2, c: U3, d, U4 }, you can't tell if that's going to be assignable to some target type without doing a computation involvingsize(U1) * size(U2) * size(U3) * ...different expansions of the type to enumerate all possible inhabitants. So we're always going to sometimes reject some of these assignments because the special-casing to allow the trivial cases has some limits.That said, I don't see why this should matter when a non-literal type appears in the target.
rbuckton commentedon Oct 23, 2024
This is the expected behavior. It is far too expensive to compute this kind of overlapping assignability for all cases, so we only currently do this work when a union has overlapping discriminant properties. A property is a discriminant property when its type consists of only unit types (e.g.,
1or"foo"). In the three failing cases above, the property in question consists of a unit type unioned with a non-unit type (string,RegExp, etc.), which means the property is not considered a discriminant and thus does not qualify for this assignability rule.In the case of
C, both types in the union contain discriminant properties as they consist only of unit types or unions of unit types (1 | 3and2 | 3).