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