镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

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

Description

@trusktr

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

  1. changed the title [-]Mapped array/tuple types not always working[/-] [+]Mapped array/tuple types not always working as expected[/+] on Mar 7, 2021
  2. RyanCavanaugh commented on Mar 8, 2021

    @RyanCavanaugh
    Member

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

  3. brabeji commented on Jul 2, 2021

    @brabeji

    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?

  4. RyanCavanaugh commented on Jul 7, 2021

    @RyanCavanaugh
    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]>
    }
  5. dtwiers commented on May 26, 2022

    @dtwiers

    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

  6. RyanCavanaugh commented on May 26, 2022

    @RyanCavanaugh
    Member

    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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs More InfoThe issue still hasn't been fully clarified

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions