How recursively get all keys of nested object?

Viewed 545
type NestedObject = {
 amount: number,
 error: string | null,
 data: {
  rows: [],
  messages: {
   goodNews: string | null,
   badNews: string | null
  }
 }
}

//trying recursively get all keys 
type AllKeys<T, K extends keyof T> = 
 T extends object ?
  T extends infer O ? 
   {keys: K | AllKeys<O, keyof O> : never
 : K

type Test = AllKeys<NestedObject, keyof NestedObject>

as a result I have:

type Test = { keys: 'amount' | 'error' | 'data' | ... }

but I need to get:

type Test = { keys: 'amount' | 'error' | 'data' | 'rows' | 'messages' | 'goodNews' | 'badNews'}
2 Answers

It looks like you want something like this:

type RecursiveKeyof<T> = T extends object ? (
    T extends readonly any[] ? RecursiveKeyof<T[number]> : (
        keyof T | RecursiveKeyof<T[keyof T]>
    )
) : never

This recurses down into object types, giving you the keys of all the subproperties and sub-subproperties, etc. It ignores primitives (so no keyof string), and special-cases arrays so that you only get keys of the elements of the array and not the array itself (which has keys like number and "push", "pop", etc).

Your AllKeys seems to want a keys property so we can write it like this:

type AllKeys<T> = { keys: RecursiveKeyof<T> }

Let's see if it works:

type Test = AllKeys<NestedObject>
// type Test = {keys: "amount" | "error" | "data" | "rows" | "messages" | "goodNews" | "badNews" }

Looks good.

Playground link to code

This works:

type NestedObject = {
 a: number,
 b: {
  bb: string[],
  cc: string,
  dd: {
    foo: string
    bar: {
      baz: 'qqq'
    }
  }
 }
}

type AllKeys<O extends object, K = O[keyof O]> =
  keyof O
  | (
    K extends any[]
      ? never
      : K extends object
        ? AllKeys<K>
        : never
  )

type Test = AllKeys<NestedObject>

Here's my playground.

Related