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

Advanced typescript: find all the values of the key "id" in a nested object

I’m trying to extract all the values of the keys "id" in the object, at the first level and at all the nested levels.

Given my ExampleType, I want NestedID<ExampleType> to be "one" | "two" | "three" | "four". However, I’m doing something wrong and it only gives me "one", and I don’t understand why.

Does somebody understands why this is not working ?

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

I got the inspiration from https://dev.to/pffigueiredo/typescript-utility-keyof-nested-object-2pa3 which is a really good tutorial.

type ExampleType = {
  id: "one"
  children: [{ id: "two" }, { id: "three"; children: [{ id: "four" }] }, { abc: "def" }]
  efg: "hij"
}

type NestedId<ObjectType extends Record<string, any> | undefined> = {
  [Key in keyof ObjectType & string]: ObjectType[Key] extends undefined
    ? never
    : Key extends "id" // if the key is "id"
      ? ObjectType[Key] // return the value of the key
      : Key extends "children" // else, if the key is "children"
        ? ObjectType[Key] extends Array<Record<string, any> | undefined> // and its value is an array of object
          ? NestedId<ObjectType[Key][number]> // recursive call
          : never // else, children value is not an object --> return never
        : never // else, key is not "id" or "children" --> return never
}[keyof ObjectType & string]

const id1: NestedId<ExampleType> = "one" // works as expected
const id2: NestedId<ExampleType> = "two" // doesn't work (Type "two" is not assignable to type "one")
const id3: NestedId<ExampleType> = "three" // doesn't work (Type "three" is not assignable to type "one")
const id4: NestedId<ExampleType> = "four" // doesn't work (Type "four" is not assignable to type "one")
const foo: NestedId<ExampleType> = "foo" // works as expected
const bar: NestedId<ExampleType> = "bar" // works as expected

>Solution :

You can use infer to achieve this. It gets the parent id and recursively checks the children props to add the ids in from there if they exist.

type NestedId<ObjectType> = 
  ObjectType extends { id: infer IdType } 
    ? IdType | (ObjectType extends { children: Array<infer ChildType> } 
      ? NestedId<ChildType> 
      : never) 
    : never;

In this example you get "one" | "two" | "three" | "four" back, and everything else like "foo" will error.

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