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 ?
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.