Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

TypeScript: Create a new type from a nested object values

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"

Ts Playground link

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

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"

Playground link to code

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading