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 tuple type should treat keys as numeric, and extract types of elements correctly #42629

Closed
AnyhowStep opened this issue Feb 3, 2021 · 3 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Feb 3, 2021

Bug Report

πŸ”Ž Search Terms

mapped tuple type, key, numeric

πŸ•— Version & Regression Information

  • TypeScript 4.1.3
  • TypeScript 3.5.1

This behaviour has been around for a very long time, I just never bothered to report it.

⏯ Playground Link

Playground with bug

Playground with workaround

πŸ’» Code

enum E {
    A,
    B,
    C,
}

type TakesE <T extends E> = ["element", T]

type Foo<
    ArrT extends E[]
> = {
    [k in keyof ArrT] : TakesE<ArrT[k]>
}

type Test = Foo<[E.A, E.B]>

πŸ™ Actual behavior

ArrT[k] assignable to E

If k is a numeric key of ArrT, then ArrT[k] is assignable to E

πŸ™‚ Expected behavior

ArrT[k] not assignable to E

Workaround

enum E {
    A,
    B,
    C,
}

type TakesE <T extends E> = ["element", T]

type TakesEHack<T extends unknown> = TakesE<Extract<T, E>>

type Foo<
    ArrT extends E[] | string
> = {
    [k in keyof ArrT] : TakesEHack<ArrT[k]>
}

type Test = Foo<[E.A, E.B]>

TakesEHack<> disables type checking at the call site but it's the most generic workaround that has worked for all my personal use cases.


Non-Workarounds

Stuff like the following didn't quite work for all my use cases,

  • k in keyof ArrT & number
    Does not give a mapped tuple type, does not extract each element's type properly
  • k in Extract<keyof ArrT, number>
    Does not give a mapped tuple type, does not extract each element's type properly
  • TakesE<Extract<ArrT[k], E>>
    This triggered complex union errors/infinitely deep type errors (because TakesE<E> is too large in my case)

I suspect Extract<> doesn't work for my case because it makes TS explore one branch per element of E.

So, TakesE<Extract<ArrT[k], E>> becomes a super large union type.

However, in my real use cases, ArrT[k] is always exactly one element of E, meaning I never have large unions in practice.


Related issues

#28093

#27995

@AnyhowStep AnyhowStep changed the title Mapped tuple type should treat keys as numeric Mapped tuple type should treat keys as numeric, and extract types of elements correctly Feb 3, 2021
@AnyhowStep
Copy link
Contributor Author

AnyhowStep commented Feb 3, 2021

As an extreme example, this takes forever to transpile on my browser,

type TakesE <T extends E> = ["element", T]

type Foo<
    ArrT extends E[]
> = {
    [k in keyof ArrT] : TakesE<Extract<ArrT[k], E>>
}

type Test = Foo<[E.AAAAA, E.AAAAB]>

enum E {
  //snip 3k+ elements
}

Probably exploring too many branches?

Playground


This takes about 18s on my browser,

type TakesE <T extends E> = ["element", T]

type TakesEHack<T extends unknown> = TakesE<Extract<T, E>>

type Foo<
    ArrT extends E[]
> = {
    [k in keyof ArrT] : TakesEHack<ArrT[k]>
}

type Test = Foo<[E.AAAAA, E.AAAAB]>

enum E {
  //snip 3k+ elements
}

Playground

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Feb 4, 2021
@RyanCavanaugh
Copy link
Member

This is a correct error; you could write

type Foo<ArrT extends E[]> = { [k in keyof ArrT] : TakesE<ArrT[k]> }
type P = E[] & { k: string };
type M = Foo<P>;

which puts string into TakesE at instantiation

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants