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
Mapped type applied to array type produces wrong toString, filter, etc typeΒ #57007
Error on doArrayStuff(x):
Argument of type 'Mappy<number[]>' is not assignable to parameter of type 'unknown[]'.
Types of property 'toString' are incompatible.
Type 'number' is not assignable to type '() => string'
changed the title [-]Mapped type applied to array type produces wrong `toString` type[/-][+]Mapped type applied to array type produces wrong `toString`, `filter`, etc type[/+]on Jan 11, 2024
This happens because, as of #48837, whenever we see a mapped type like type Mappy<T extends unknown[]> = { [K in keyof T as K]: T[K] } where the modifier type (T) is an array or tuple, we add a numeric constraint to K in the template type. So the mapped type is internally represented as something like this: type Mappy<T extends unknown[]> = { [K in keyof T as K]: T[K & (number | ${number})] }.
When we try to get the type of property toString of type Mappy<number[]>, what we do is instantiate the mapped type's template type, T[K & (number | ${number})], with T = number[] and K = "toString". So T[K & (number | ${number})] becomes (number[])["toString" & (number | ${number})], which resolves to (number[])[never]. Type number[] has an index signature mapping numbers to numbers, so (number[])[never] resolves to number via this index signature.
The same happens for the other array methods and they all end up having type number.
This problem is not present when you remove the as K clause in the mapped type, i.e. because then we resolve the mapped type Mappy<number[]> in a special way that directly creates an array type as a result, instead of the normal instantiation process described above.
Activity
DanielRosenwasser commentedon Jan 10, 2024
These say they were scheduled for 4.5, but I wonder if this is related to #46218 and #46169.
gabritto commentedon Jan 10, 2024
Bisected to #48837
[-]Mapped type applied to array type produces wrong `toString` type[/-][+]Mapped type applied to array type produces wrong `toString`, `filter`, etc type[/+]gabritto commentedon Jan 11, 2024
This happens because, as of #48837, whenever we see a mapped type like
type Mappy<T extends unknown[]> = { [K in keyof T as K]: T[K] }where the modifier type (T) is an array or tuple, we add a numeric constraint toKin the template type. So the mapped type is internally represented as something like this:type Mappy<T extends unknown[]> = { [K in keyof T as K]: T[K & (number | ${number})] }.When we try to get the type of property
toStringof typeMappy<number[]>, what we do is instantiate the mapped type's template type,T[K & (number | ${number})], withT=number[]andK="toString". SoT[K & (number | ${number})]becomes(number[])["toString" & (number | ${number})], which resolves to(number[])[never]. Typenumber[]has an index signature mapping numbers to numbers, so(number[])[never]resolves tonumbervia this index signature.The same happens for the other array methods and they all end up having type
number.This problem is not present when you remove the
as Kclause in the mapped type, i.e. because then we resolve the mapped typeMappy<number[]>in a special way that directly creates an array type as a result, instead of the normal instantiation process described above.#55774 would fix this.