Skip to content

Mapped tuple type should treat keys as numeric, and extract types of elements correctlyΒ #42629

Closed
@AnyhowStep

Description

@AnyhowStep

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions