2

I'm trying to write a function that will take an array of initialisation functions, along with their options. I am able derive the type of the options from the function, so I thought I should therefore be able to check that the options I provide are of the same type.

The example is a minimised version of this. I think I need an independent generic for each array item, but I obviously don't know the size of the array.

type O<T> = { a: T }
type E<T> = [O<T>, T]

const f = <T={}>(array: E<T>[]) => {}

f([
  [{ a: 4 }, 4],     //Works
  [{ a: "5" }, "5"]   //Doesn't work as T is set to number
])
WayneC
  • 5,569
  • 2
  • 32
  • 43
Edward Kotarski
  • 594
  • 5
  • 11
  • Possible duplicate of [How do you define an array of generics in TypeScript?](https://stackoverflow.com/questions/51879601/how-do-you-define-an-array-of-generics-in-typescript) – jcalz Feb 04 '19 at 00:34

1 Answers1

0

The solution:

type O = { a: any }

type E<T extends O[]> = {
   [P in keyof T]: T[P] extends T[number] ? [T[P], T[P]["a"]] : never;
}  

const f = <T extends O[]>(...args: E<T>) => { }  

Or more generically as:

type E<T extends {}[], K extends keyof T[number]> = {
   [P in keyof T]: T[P] extends T[number] ? [T[P], T[P][K]] : never;
}

const f = <T extends O[]>(...args: E<T, "a">) => { }

It should be noted that I could not get this to work without using the rest parameter.

Explanation:

Once I stopped focusing so much on a single generic for each element, using mapped types I managed to get to:

type E<T extends { a: any }[]> = {
   [P in keyof T]:  [T[P], T[P]["a"]]
}

But got an error: Type '"a"' cannot be used to index type 'T[P]'.

This turns out to be because using keyof with tuples includes non-number keys (e.g. 'length'). I found the solution on a Typescript Github issue; only check the numeric keys.

Edward Kotarski
  • 594
  • 5
  • 11