What is the best way to retrieve all keys from a deeply nested object using recursion

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

//attempting to recursively retrieve 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 got:

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

however, I aim to obtain:

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

Answer №1

Seems like you're interested in something along these lines:

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

This function delves into object types, providing you with the keys of all nested properties and sub-properties. It excludes primitives (such as keyof string), and handles arrays differently by focusing on the keys of the array elements rather than the array itself (which would include keys like number and "push", "pop", etc).

Your AllKeys function appears to require a keys property, so it can be implemented as follows:

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

Let's test it out:

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

Looks good.

Playground link to code

Answer №2

This code snippet is functioning perfectly:

interface 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>

Take a look at my TypeScript playground.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it possible to determine the time format preference of the user's device in Angular? For example, whether they use a 24-hour system or a 12-hour system with AM

In Angular, is there a way to determine whether the user's time format is set to 24-hour or 12-hour system? Any help would be greatly appreciated. Thanks! ...

What is the reason behind TypeScript failing to provide type safety in a generic higher order component which introduces extra properties into React components?

I'm currently working on developing a versatile higher order component, but have encountered an issue with type safety not being enforced. Interestingly, when attempting the same implementation without using generics, the type safety is verified as ex ...

Is there a way to verify within the "if" statement without repeating the code?

Is there a way in javascript (or typescript) to prevent redundant object rewriting within an if statement condition? For example: if (A != null || A != B) { // do something here } // can it be done like this: if (A != null || != B) { // avoid repeating ...

Organizing Angular models and interfaces

The Angular styleguide provides best practices for using classes and interfaces in applications, but it does not offer guidance on organizing interfaces and model classes. One common question that arises is: what are the best practices for organizing file ...

Encountered an issue while attempting to assign a value to a property that is declared as [key in keyof T]

I am currently working on implementing a function that selects specific properties of an object. Here is the current function: const project = function<T>(object: T, projection: Projection<T>): Partial<T> { throw new Error("not imple ...

Guide to creating a Map with typescript

I've noticed that many people are converting data to arrays using methods that don't seem possible for me. I'm working with React and TypeScript and I have a simple map that I want to render as a list of buttons. Here is my current progres ...

The union type consisting of String, Boolean, and Number in type-graphql has encountered an error

I attempted to create a union type in type-graphql that represents the String, Number, and Boolean classes, but unfortunately, it was not successful. Does anyone have any suggestions on how to achieve this? export const NonObjectType = createUnionType({ ...

Tips for incorporating conditional types into function parameters based on existing input

The title might be confusing, so allow me to clarify. My Objective I am referring to the code snippet below. I aim to specify the route property based on the types property as either a string or a function that returns a string. The Code Let's b ...

Whenever I attempt to make changes to the React state, it always ends up getting reset

Currently, I am attempting to utilize Listbox provided by Headless UI in order to create a select dropdown menu for filtering purposes within my application. However, the issue I have encountered is that whenever I update my "selectedMake" state, it revert ...

Configuring a NestJS application to establish a TypeOrm connection using environment variables and @nestjs/config

Looking for the best way to set up a NestJS database using a .env file in compliance with legal requirements. The goal is to utilize the @nestjs/config package to import .env variables and incorporate them into the TypeOrmModule. It appears that utilizing ...

Transform array of elements from type T1 to element in the array to type T2

Here is a Typescript class I am working with: export class Envelope<T> { result: T; constructor(result: T) { this.result = result; } } I'm trying to convert Envelope<RecentPostResponse[]> to Observable<PostModel[]>: getP ...

Error Encountered during Compilation of React TypesIs this okay

Currently, I am working on an MVC project that involves the use of TypeScript. To access the types required for this project, I have also integrated React. To obtain the React types, I performed an npm install --save-dev @types/react (similarly for react-d ...

Showcasing an input field once a specific button is pressed in an Angular application

When triggered, I am looking for a blank panel that will display a text box within the same panel. This functionality should be implemented using Angular. ...

An unhandled promise rejection occurred because no routes could be found to match. URL Segment:

I'm facing an issue with my application where it doesn't recognize the route even though I have defined and imported it in app.module. Whenever I try to redirect to a specific route upon data retrieval, I encounter this exception: onSubmit(){ ...

Compile time extraction of constant string from type field

I am currently facing an issue with a field in my type that contains a constant string literal. My goal is to be able to reference both the type and field by name so that I can utilize this string literal throughout my code. Here is an example: export type ...

What causes TypeScript to convert a string literal union type to a string when it is assigned in an object literal?

I am a big fan of string literal union types in TypeScript. Recently, I encountered a situation where I expected the union type to be preserved. Let me illustrate with a simple example: let foo = false; const bar = foo ? 'foo' : 'bar' ...

The implementation of Symbol.species in the Node.js Buffer class to generate a RapidBuffer seems illogical and confusing

While exploring the source code of ws, a popular WebSocket implementation for Node.js, I stumbled upon this specific piece of code: const FastBuffer = Buffer[Symbol.species]; But what exactly is this FastBuffer used for? Surprisingly, it seems that they a ...

The discord.js TypeScript is throwing an error stating that the 'index.ts' file is missing when trying to run 'ts-node index.ts'

I have been working on creating a discord bot using discord.js and TypeScript. However, when I attempt to start the bot by running 'ts-node index.ts', I encounter the following error: Error: Cannot find module 'node:events' Require stac ...

Error: setPosition function only accepts values of type LatLng or LatLngLiteral. The property 'lat' must be a numerical value in agm-core agm-overlay

Currently, I am utilizing Angular Maps powered by Google @agm-core and agm-overlay to implement custom markers. However, when using the (boundsChange) function on the agm-map component, an error occurs with the message "invalidValueError: setPosition: not ...

Troubleshooting asynchronous problems with rxjs chaining within ngrx effects

@Effect({ dispatch: false }) public setJwtDataParcoursPage = this.actions$.pipe( ofType(INIT_FORM_SUCCESS_ACTION), map((action: InitFormSuccessAction) => action.payload), withLatestFrom(this.store.select(this._apiHeadersSelector.getJwt) as Observa ...