Is there a way to utilize const assertions to retrieve the explicit types from objects nested at various levels?

In reference to this question, the previous structure had a depth of 2:

const grandkids = {
  Karen: {
    Ava: ['Alice', 'Amelia'],
    Emma: ['Sarah'],
  },
  Mary: {
    Sophia: ['Grace'],
  },
} as const;

To extract literal types from the example above, you can use:

type Grandmother = keyof typeof grandkids; // Karen|Mary

type Mother = {
  [K in Grandmother]: keyof typeof grandkids[K];
}[Grandmother]; // Ava|Emma|Sophia

type Kid = {
  [K in Grandmother]: {
    [M in Mother]: typeof grandkids[K][M extends keyof typeof grandkids[K] ? M : never];
  };
}[Grandmother][Mother][number]; // Alice|Amelia|Sarah|Grace

But what about structures with 3 levels?

const greatgrandkids = {
  Beth: {
    Karen: {
      Ava: ['Alice', 'Amelia'],
      Emma: ['Sarah'],
    },
    Mary: {
      Sophia: ['Grace'],
    },
  },
  Ruth: {
    Ella: {
      Scarlett: ['Charlotte'],
    },
  },
};

For the structure above, which includes a great-grandmother, there is an additional type for the first level:

type GreatGrandmother = /* CODE?? */; // Beth|Ruth
type Grandmother = ... ; // Karen|Mary|Ella
type Mother = // ...

This pattern continues for any number of levels. Ensuring consistent depth with complete levels down to the leaf fields is important.

Answer №1

If you're seeking a way to obtain the union of string literal types corresponding to keys at a specific depth within a structure, consider using a utility type called KeysAtDepth<T, N>. This utility type allows you to retrieve keys directly from T, its properties, sub-properties, and so on based on the specified depth.

Here's an example implementation:

type KeysAtDepth<T, N extends number, A extends any[] = []> =
  T extends unknown ? (
    N extends A['length'] ? keyof T : KeysAtDepth<T[keyof T], N, [0, ...A]>
  ) : never;

This is a recursive conditional type in TypeScript, cleverly utilizing tuple types and length checking to simulate numeric operations. By making use of this method, you can effectively recurse until reaching the desired depth in the structure.

In order to ensure that KeysAtDepth<T, N> behaves as a distributive conditional type in T, enabling it to work properly with unions, the expression T extends unknown ? ⋯ : never is included.


You can test this approach with the following code snippet:

const greatgrandkids = { /* Nested object structure here */ } as const;

type GreatGrandKids = typeof greatgrandkids;
type GreatGrandMother = KeysAtDepth<GreatGrandKids, 0>
// Output: "Beth" | "Ruth"
type GrandMother = KeysAtDepth<GreatGrandKids, 1>
// Output: "Karen" | "Mary" | "Ella"
type Mother = KeysAtDepth<GreatGrandKids, 2>;
// Output: "Ava" | "Emma" | "Sophia" | "Scarlett"

The next step involves handling non-uniform structures by defining another utility type TypesAtDepth<T, N> for extracting union types at a particular depth. With this utility type, you'll be able to access and work with nested types effortlessly.

Here's how you can implement TypesAtDepth<T, N>:

type TypesAtDepth<T, N extends number, A extends any[] = []> = 
  T extends unknown ? (
    N extends A['length'] ? T : TypesAtDepth<T[keyof T], N, [0, ...A]>
  ) : never;

Using TypesAtDepth, you can effectively handle scenarios where the structure may not follow a consistent pattern all the way down. The final type obtained would reflect the union of element types based on the specified depth level.

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

Necessary Typescript class property when executing code

Is there a way to determine if a class property is required in Typescript at runtime? export class A { public readonly ab?: number; public readonly ac?: number; public readonly ad: number; public readonly ae: number; } Can emitDecoratorMetadata o ...

React / NextJS: Repeating Audiowave Component

I am currently developing a chat application in NextJS that includes text-to-speech functionality. To visualize the audio playback waveform, I have integrated a third-party library called wavesurfer.js Though the audio implementation is functioning proper ...

Removing an item from an array depending on a specific condition and index

I am working with an array structure that looks like this: [ {key: 4, data: {…}, qText: {…}}, {key: 4, data: {…}, qText: {…}, isVisible: false}, {key: 5, data: {…}, qText: {…}, isVisible: false}, {key: 4, data: {…}, qText: {…}, isVi ...

Tips for accessing a variable from a Global service in Ionic

I am currently working on developing an app using Ionic but experiencing some difficulties. I encountered an issue while trying to access a variable from a global service when it is imported to another page. Here is an example of the Global service (backen ...

Attempting to add a new property to every object in an array using TypeScript

In my Angular project, I have a specific code block that retrieves an array of objects containing contact records. Within a 'forEach' loop, I am generating a new field for each contact record to store the user's initials. The goal is to ad ...

Angular jsonp.get request was denied despite receiving a status code of 200 indicating success

I have been attempting to access my basic web API created in Jersey, which returns the following data: [ { "id": 1, "name": "Facebook", "userName": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f4 ...

Interactive loadChild components

I've been attempting to dynamically import routes from a configuration file using the following code snippet: export function buildRoutes(options: any, router: Router, roles: string[]): Routes { const lazyRoutes: Routes = Object.keys(options) ...

Exporting multiple sheets using Angular's ngx-export-as functionality

Currently utilizing ngx-export-as in my Angular project and I am looking to export an Excel file with multiple sheets. How can I achieve this export functionality? I have raised a concern regarding this on GitHub. ...

Waiting for the execution of the loop to be completed before proceeding - Typescript (Angular)

There's a code snippet triggered on an HTML page when clicked: public salaryConfirmation() { const matDialogConfig: MatDialogConfig = _.cloneDeep(GajiIdSettings.DIALOG_CONFIG); this.warningNameList = []; for(let i=0; i < this.kelolaDat ...

Deleting a file from the assets folder in Angular for good

I am attempting to permanently delete a JSON file from the assets folder using my component. Despite trying to use HttpClient, I encounter no errors but the file remains undeleted. constructor(http: HttpClient){} remove() { this.http.delete('assets ...

I am having trouble viewing the input value on my Angular5 form

Here is the HTML code snippet that I've written: <input type="text" name="fechainscripcion" #fechainscripcion="ngModel" [(ngModel)]="alumno.fechainscripcion" value="{{today | date:'dd/MM/yyyy'}}" class="form-control" /> This is a seg ...

The reason behind the clickable issue with my duplicate <ion-select>

I've been working on a form using HTML, CSS, and JavaScript where I implemented an "Add" button to duplicate the form. The issue arises with the ion-select element within the duplicated form. While the original form displays all predefined options upo ...

Unable to access pathways from a separate source

In my app.component.ts file, I have two router outlets defined, one with the name 'popup': @Component({ selector: 'app-main', template: `<router-outlet></router-outlet> <router-outlet name="popup" ...

Nested HTTP requests in Angular using RxJS: Triggering component update after completion of the first HTTP request

I have a requirement to make two http requests sequentially. The values retrieved from the first call will be used in the second call. Additionally, I need to update my component once the first http request is completed and also update it once the second ...

Is there a way to utilize Angular in identifying whether a given value corresponds with the value of a particular radio button within my application?

I have an array of lists that I need to display in radio buttons. Like this: https://i.stack.imgur.com/cmYgO.png https://i.stack.imgur.com/Zx4bm.png https://i.stack.imgur.com/jBTe3.png My goal is to have a checkbox that matches the values loaded from a ...

Tips for selecting the best className type for material-ui components

Currently, I am integrating material-ui into a react app that is built using typescript. Within the material-ui framework, there is a feature called withStyles which allows styles to be injected into a component through its className. However, I am facing ...

Encountering an issue with Nuxt 3.5.1 during the build process: "ERROR Cannot read properties of undefined (reading 'sys') (x4)"

I am currently working on an application built with Nuxt version 3.5.1. Here is a snippet of the script code: <script lang="ts" setup> import { IProduct } from './types'; const p = defineProps<IProduct>(); < ...

Create a keyup function that triggers an alert message if the user's input does not meet the

Hello, I'm looking for some assistance with a coding problem. Basically, I have an array of numbers which includes 5, 8, and 10. I need to create a form where users can input numbers. If the user inputs a number that is not 5, 8, or 10, I want to disp ...

Creating a unique Angular 2 Custom Pipe tutorial

I've come across various instances of NG2 pipes online and decided to create one myself recently: @Pipe({name: 'planDatePipe'}) export class PlanDatePipe implements PipeTransform { transform(value: string): string { return sessionStor ...

Following the recent update to webpack-dev-server and webpack, certain modules are being requested that do not exist in the project

Recently, I made updates to my project that involved Vue.js and Typescript. After updating webpack and webpack-dev-server, I encountered a problem where certain modules were missing when attempting to run the project in development mode. Here is some addi ...