Skip to content
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

Mapped array/tuple types not always working as expected #43129

Closed
trusktr opened this issue Mar 7, 2021 · 5 comments
Closed

Mapped array/tuple types not always working as expected #43129

trusktr opened this issue Mar 7, 2021 · 5 comments
Labels
Needs More Info The issue still hasn't been fully clarified

Comments

@trusktr
Copy link

trusktr commented Mar 7, 2021

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.

@trusktr trusktr changed the title Mapped array/tuple types not always working Mapped array/tuple types not always working as expected Mar 7, 2021
@RyanCavanaugh
Copy link
Member

It seems like you figured this out correctly. What part of this is supposed to be the defect?

@RyanCavanaugh RyanCavanaugh added the Needs More Info The issue still hasn't been fully clarified label Mar 8, 2021
@brabeji
Copy link

brabeji commented Jul 2, 2021

@RyanCavanaugh @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
Copy link
Member

keyof MyTuple includes things like "indexOf" because tuples are arrays. This is the expected behavior.

You can write this instead:

type Box<T extends string> = { value: T }
type MyTuple = [string, ...string[]]
type A = {
    [K in (keyof MyTuple) & number]: Box<MyTuple[K]>
}

@dtwiers
Copy link

dtwiers commented May 26, 2022

Is this the same issue that is catching me here?

type SomeAbstractedType = string;

type SpecialObject<T extends SomeAbstractedType> = { foo: T }

type SpecialObjectsOf<Ts extends SomeAbstractedType[]> = {
    [K in keyof Ts]: SpecialObject<Ts[K]>;
//                                 ^^^^^
// Type 'Ts[K]' does not satisfy the constraint 'string'.
//   Type 'Ts[keyof Ts]' is not assignable to type 'string'.
//     Type 'Ts[string] | Ts[number] | Ts[symbol]' is not assignable to type 'string'.
//       Type 'Ts[string]' is not assignable to type 'string'.(2344)
};

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 MyTupleType not necessarily being number. I'm not sure when I'd ever want keyof MyTupleType to include 'length or 'indexOf' - perhaps if I want that I would put keyof Array.prototype or 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:

image

@RyanCavanaugh
Copy link
Member

@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

@RyanCavanaugh RyanCavanaugh closed this as not planned Won't fix, can't repro, duplicate, stale May 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs More Info The issue still hasn't been fully clarified
Projects
None yet
Development

No branches or pull requests

4 participants