'Map Typescript generic array

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';
}


Solution 1:[1]

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.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1