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.