1

I am trying to write a type level function Foo which will get the types of the first elements of a "two dimensional" array. I have written the following

type Foo<A extends any[][]> = {
    [I in keyof A]: First<A[I]>
}

type First<A extends any[]> = A[0]

This code does not compile with the following error

Type 'A[I]' does not satisfy the constraint 'any[]'.
  Type 'A[keyof A]' is not assignable to type 'any[]'.
    Type 'A[string] | A[number] | A[symbol]' is not assignable to type 'any[]'.
      Type 'A[string]' is not assignable to type 'any[]'.

which I am struggling to understand. Specifically, where A[string] | A[number] | A[symbol] comes from. It is my understanding that mapped arrays should allow me to index the elements of the array which should be more arrays. I can work around this issue with conditional types by defining First like so

type First<A> =
    A extends any[] ? A[0] :
    never;

but I do not understand why this is necessary.

1 Answer 1

2

It's pretty obnoxious, I know. It is marked as a bug. The underlying issue seems to be that the compiler only realizes it's mapping an array/tuple when you use the mapped type, not when you define it.

// here the compiler doesn't know that I will be a numeric-like key:
type Foo<A extends any[][]> = { [I in keyof A]: Extract<A[I], any[]>[0] };

// only here does the compiler perform the mapping with just the numeric-like keys:
type Expected = Foo<[[1, 2], [3, 4], [5, 6], [7, 8]]>;
// type Expected = [1, 3, 5, 7]

It also turns out that it's possible to make the compiler map an array type as a plain object, including those non-numeric-like keys such as length, join, etc.:

type Unexpected = Foo<[[1, 2], [3, 4], [5, 6], [7, 8]] & { a: string }>;
/* type Unexpected = {
    [x: number]: 1 | 3 | 5 | 7;
    0: 1;
    1: 3;
    2: 5;
    3: 7;
    length: never;
    toString: never;
    toLocaleString: never;
    pop: never;
    push: never;
    concat: never;
    join: never;
    reverse: never;
    shift: never;
    slice: never;
    sort: never;
    ... 18 more ...;
    a: never;
}
*/

The fact that this is even possible means that the compiler can't assume that A[I] is assignable to any[], so you're currently stuck with having to do something like Extract<A[I], any[]> (which is similar to your fix).

Hope that helps; good luck!

Link to code

Sign up to request clarification or add additional context in comments.

1 Comment

Oh I see. Thanks for the link to the ticket and the Extract suggestion.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.