Repository navigation
Mapped array/tuple types not always working as expectedΒ #43129
Description
Bug Report
π Search Terms
Mapped array types
π Version & Regression Information
I noticed now in TS 4.1.2.
β― Code and Playground Links
There are two problems.
The following has an error in the mapped type despite that I did it just like in #26063 (as far as I know):
type Constructor<T = object, A extends any[] = any[], Static = {}> = (new (...a: A) => T) & Static
type ElementTypeArrayToInstArray<T extends Constructor[]> = {
[K in keyof T]: InstanceType<T[K]> // <----------- ERROR
}
type ArrayValues<T extends any[]> = T[keyof T]
type ElementTypes<T extends Constructor[]> = ArrayValues<ElementTypeArrayToInstArray<T>>
type test = ElementTypes<[typeof HTMLAnchorElement, typeof HTMLDivElement]>Playground link with relevant code
But notice that when you hover on test, it is a union of all the values of the array including values from non-numeric keys, which is unlike the examples in #26063.
There seems to some sort of special casing happening.
I had to specify that I want number keys only in order(unlike #26063) to get rid of the error:
type Constructor<T = object, A extends any[] = any[], Static = {}> = (new (...a: A) => T) & Static
type ElementTypeArrayToInstArray<T extends Constructor[]> = {
[K in keyof T]: InstanceType<T[number & K]> // <----------- GOOD
}
type ArrayValues<T extends any[]> = T[keyof T]
type ElementTypes<T extends Constructor[]> = ArrayValues<ElementTypeArrayToInstArray<T>>
type test = ElementTypes<[typeof HTMLAnchorElement, typeof HTMLDivElement]>Playground link with relevant code
But notice that test still contains all the values of all keys, not just array values.
Finally, I had to specify numeric keys for ArrayValues:
type Constructor<T = object, A extends any[] = any[], Static = {}> = (new (...a: A) => T) & Static
type ElementTypeArrayToInstArray<T extends Constructor[]> = {
[K in keyof T]: InstanceType<T[number & K]>
}
type ArrayValues<T extends any[]> = T[number & keyof T] // <-------- number
type ElementTypes<T extends Constructor[]> = ArrayValues<ElementTypeArrayToInstArray<T>>
type test = ElementTypes<[typeof HTMLAnchorElement, typeof HTMLDivElement]>Playground link with relevant code
But notice if I use Promise instead of InstanceType, the first issues goes away, although number is still needed in ArrayValues:
type Constructor<T = object, A extends any[] = any[], Static = {}> = (new (...a: A) => T) & Static
type ElementTypeArrayToInstArray<T extends Constructor[]> = {
[K in keyof T]: Promise<T[K]> // <-------- no number
}
type ArrayValues<T extends any[]> = T[number & keyof T] // <-------- number still needed
type ElementTypes<T extends Constructor[]> = ArrayValues<ElementTypeArrayToInstArray<T>>
type test = ElementTypes<[typeof HTMLAnchorElement, typeof HTMLDivElement]>Playground link with relevant code
π Actual behavior
Does not work like #26063
π Expected behavior
Works like #26063
There seems to be some sort of special casing going on.
Activity
[-]Mapped array/tuple types not always working[/-][+]Mapped array/tuple types not always working as expected[/+]RyanCavanaugh commentedon Mar 8, 2021
It seems like you figured this out correctly. What part of this is supposed to be the defect?
brabeji commentedon Jul 2, 2021
Ryan Cavanaugh (@RyanCavanaugh) Joe Pea (@trusktr) Hello guys. Just ran into this. From what Iβm able to say, mapping tuples doesnβt seem to work as in #26063
Minimal reproduction playground
What am I missing?
RyanCavanaugh commentedon Jul 7, 2021
keyof MyTupleincludes things like"indexOf"because tuples are arrays. This is the expected behavior.You can write this instead:
dtwiers commentedon May 26, 2022
Is this the same issue that is catching me here?
If so, what it boils down to, seems to be this: I don't think we can do type constraints on mapped tuples...due to (imo) unintuitive behavior of
keyof MyTupleTypenot necessarily beingnumber. I'm not sure when I'd ever wantkeyof MyTupleTypeto include'lengthor'indexOf'- perhaps if I want that I would putkeyof Array.prototypeor something.Relevant TS playground:
https://www.typescriptlang.org/play?ts=4.6.4#code/C4TwDgpgBAyg9gWwgQQEYGdgCcCGBjYCAEwBVxoBeKTLASwDsBzAbgFgAoD0SWSPWnABsA8qgBWEAgB4SUCAA9C9IuliIUGbPkKlyAPihUA3lABmcOAC4osgL4cu5XpIEjxk4OmGmZqhUpU1JDQabWIySABtAF0DYw4oRKhIgGkoBigAawgQOFMbdGjrGD5XUQlpEnRU2LZ2WzqOIklBHCxoPDh6TDMLa0iAcnM4AYAaKAHUNoHoxvZmvFb2qE7u4CgprGLSoXKPLx9uCDzeuD0gA
In the playground, I'm able to get values of the mapped type and they're exactly what I want, but the definition of them still errors out:
RyanCavanaugh commentedon May 26, 2022
Derek Wiers (@dtwiers) looks like that's been fixed; there's no error in TypeScript 4.7
Closing since there doesn't seem to be anything actionable left