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
Unions of objects/dictionaries/maps and tuples generate type errors when numeric keys are present #13179
Expected behavior:
boxE is seen as an object containing tuples "hey", "world" and "0".
Actual behavior:
[ts]
Type '{ "0": boolean[]; hey: [true, false]; world: [false, true]; }' is not assignable to type 'BoxOfBoolPairs'.
Type '{ "0": boolean[]; hey: [true, false]; world: [false, true]; }' is not assignable to type '[boolean, boolean]'.
Types of property '0' are incompatible.
Type 'boolean[]' is not assignable to type 'boolean'.
The error message is not the best here, which is a problem we have with unions from time to time. The compiler thinks things that have a "0" property are tuples, so it has compared boxE to the [boolean, boolean] branch of BoxOfBoolPairs, not the string indexer branch.
That's because tuples are really just aliases for a family of types that look kind of like this:
So what happens is that the compiler sees the '0', tries to infer T from [false, true], tries to infer U from the non-existent '1' property and creates a Tuple<boolean[], {}>. This fails to match Tuple<boolean, boolean> and creates an error.
I'll go see why the compiler doesn't explore the string indexer branch and find a match.
Looks like contextual type applies to hey to give the type [true, false] but 0 has the contextual type boolean[], which is then not assignable to either side of BoxOfBoolPairs.
The type of '0' is taken from the contextual type BoxOfBoolPairs, which finds a property '0': boolean in the [boolean, boolean] side of the union.
The type of 'hey' is taken from the contextual type BoxOfBookPairs, which does not find a matching property hey, so falls back on the string indexer type.
I need to think about a fix; finding a specific property name 0 should definitely outweigh the type of the string indexer. But it does the wrong thing in this case.
After talking to Ryan Cavanaugh (@RyanCavanaugh), I don't think there is a way to create values of type BoxOfBoolPairs in Typescript. Because of the way tuples and arrays are defined in Javascript as objects with number-string keys, it's not possible to distinguish the two sides of the union when given a value.
The correct fix is to use the ADT pattern and add a discriminant field, something like kind, to distinguish between leaf and non-leaf instances of the type.
Activity
sandersn commentedon Apr 24, 2017
The error message is not the best here, which is a problem we have with unions from time to time. The compiler thinks things that have a
"0"property are tuples, so it has comparedboxEto the[boolean, boolean]branch ofBoxOfBoolPairs, not the string indexer branch.That's because tuples are really just aliases for a family of types that look kind of like this:
So what happens is that the compiler sees the '0', tries to infer
Tfrom[false, true], tries to inferUfrom the non-existent '1' property and creates aTuple<boolean[], {}>. This fails to matchTuple<boolean, boolean>and creates an error.I'll go see why the compiler doesn't explore the string indexer branch and find a match.
sandersn commentedon Apr 25, 2017
Looks like contextual type applies to
heyto give the type[true, false]but0has the contextual typeboolean[], which is then not assignable to either side ofBoxOfBoolPairs.The type of '0' is taken from the contextual type
BoxOfBoolPairs, which finds a property'0': booleanin the[boolean, boolean]side of the union.The type of 'hey' is taken from the contextual type
BoxOfBookPairs, which does not find a matching propertyhey, so falls back on the string indexer type.I need to think about a fix; finding a specific property name
0should definitely outweigh the type of the string indexer. But it does the wrong thing in this case.sandersn commentedon Apr 25, 2017
After talking to Ryan Cavanaugh (@RyanCavanaugh), I don't think there is a way to create values of type
BoxOfBoolPairsin Typescript. Because of the way tuples and arrays are defined in Javascript as objects with number-string keys, it's not possible to distinguish the two sides of the union when given a value.The correct fix is to use the ADT pattern and add a discriminant field, something like
kind, to distinguish between leaf and non-leaf instances of the type.