Home > other >  Map Typescript generic array
Map Typescript generic array

Time:05-14

Suppose I have an object with a static set of keys, and then a type specifying a subset of those keys as a static array:

const myBigStaticObject = {
  key1: () => 'foo',
  key2: () => 'bar',
  // ...
  keyN: () => 'fooN',
}

type BigObject = typeof myBigStaticObject

type Keys = ['key2', 'keyN']

Is there a nice way of then programmatically creating a mapped type which takes Keys as its generic argument, i.e. does the following:

type ObjectValueFromKeys<K extends (keyof BigObject)[]> = // ???

// such that:

ObjectValueFromKeys<Keys> = [() => 'bar', () => 'fooN']

The equivalent for objects here would be:

const objectKeys = {
  a1: 'key2';
  a2: 'keyN';
}

type ObjectValueFromObjectKeys<M extends Record<string, keyof BigObject>> = {
  [K in keyof M]: BigObject[M[K]]
}

// then

type ObjectValueFromObjectKeys<typeof objectKeys> = {
  a1: () => 'bar';
  a2: () => 'fooN';
}

CodePudding user response:

You may not know this but you can actually use a mapped type with arrays and tuples while still retaining the array/tuple type in the output!

That means you can use something like this, where it checks if the value is a key of the big object first:

type ObjectValueFromKeys<K extends (keyof BigObject)[]> = {
    [P in keyof K]: K[P] extends keyof BigObject ? BigObject[K[P]] : K[P];
};

And it works like a charm:

type T1 = ObjectValueFromKeys<["key2", "keyN"]>;
//   ^? [() => "bar", () => "fooN"]

Don't believe me? See for yourself.

You will probably notice that there is as const used in the big object:

    key1: () => 'foo' as const,

This is because, for some reason, TypeScript can't deduce that this function can only return "foo". And so I have used as const to make it return the literal "foo" for our case so we can see if the solution works.

  • Related