Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # 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.

# for free to join this conversation on GitHub. Already have an account? # 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