I'm trying to create a new type from nested object value but if the key is not present on any of the nested levels, it is failing, Where I'm going wrong?
const events = [
{ name: 'foo' },
{ name: 'bar' },
{ value: 'baz' }
] as const;
type Events = typeof events;
type AllValuesOf<T> = T extends any ? T[keyof T] : never;
// Error: Property 'name' does not exist on the type
type Name = AllValuesOf<Events>['name'] // ideally I want this to be 'foo' | 'bar'
Expected
type Name = "foo" | "bar"
It looks like you'd like to take Events
and produce a type of the following form, programmatically:
type AllEvents = {
name: "foo" | "bar";
value: "baz";
}
That way you could index into it with "name"
to get the union "foo" | "bar"
, or index into it with "value"
to get "baz"
.
If we start with a union T
of object types (expected to be Events[number]
, the union of elements of events
), then we first need to find the full set of its keys. We can do this by distributing the keyof
operator over unions in T
:
type AllKeys<T> = T extends unknown ? keyof T : never;
Then we can Combine
them into a single object type. For each key K
in AllKeys<T>
, we filter the T
union for just those members with key K
, and grab the value type at that key. This is another job for distributive conditional types, also using the infer
keyword to do the "grabbing":
type Combine<T> =
{ [K in AllKeys<T>]: T extends Record<K, infer V> ? V : never };
Put that together and we get the desired AllEvents
type:
type AllEvents = Combine<Events[number]>;
/* type AllEvents = {
name: "foo" | "bar";
value: "baz";
} */
And you can index into it as desired:
type Name = AllEvents['name'];
//type Name = "foo" | "bar"