What is the process for defining a recursive array data structure?

Looking to implement a TypeScript method that can navigate through nested arrays of strings when given an instance of a type/class/interface. The method, in concept, should resemble:

method<T>(instance: T, arrayAttr: someType) {
    let tmp = undefined;
    for (let attr in arrayAttr) {
        tmp = instance[attr];
    }     
    return tmp;
}

I want to designate the someType to enforce type checking and generate errors if an attribute does not exist within the instance.

For example, with the object:

const obj = {
 attribute1: {
     attribute2: {
        attribute3: "value"
     }
  }
};

The method call:

method(obj, ['attribute1', 'attribute2', 'attribute3'])

should yield 'value'. Conversely, invoking the method with incorrect attributes:

method(obj, ['attribute1', 'attribute3'])

it should result in a compilation error.

One attempt I made was with this snippet:

type NestedAttributes<T extends InstanceType, U extends keyof T> =
    U extends keyof T ? T[U] extends InstanceType ? NestedAttributes<T[U], keyof T[U]> : T[U] : never;

However, this solution proved less than ideal.

Answer №1

Typically, in such scenarios, the best approach may not always be the most elegant one. For instance, determining N overloads up to the maximum required number of parameters can be a solution. f.e.:

function method<T, U extends keyof T>(obj: T, attr: U): T[U];
function method<T, U1 extends keyof T, U2 extends keyof T[U1]>(obj: T, attr1: U1, attr2: U2): T[U1][U2];
// and so forth...

Additionally, you can incorporate an "internal" overload to facilitate recursive calls:

function method(obj: any, ...params: never[]): never;

Therefore, the complete code for 3 parameters would resemble this:

function method<T, U extends keyof T>(obj: T, attr: U): T[U];
function method<T, U1 extends keyof T, U2 extends keyof T[U1]>(obj: T, attr1: U1, attr2: U2): T[U1][U2];
function method<T, U1 extends keyof T, U2 extends keyof T[U1], U3 extends keyof T[U1][U2]>(obj: T, attr1: U1, attr2: U2, attr3: U3): T[U1][U2][U3];
//...
function method(obj: any, ...params: never[]): never;
function method(obj: any, ...params): any
{
    const currentKey = params[0];
    let res = obj[currentKey];
    const restArgs = params.slice(1) as never[];
    if (params.length > 1) res = method(res, ...restArgs);
    return res;
}


const obj = {
    attribute1: {
        attribute2: {
            attribute3: "value"
        }
    }
};

const a = method(obj, 'attribute1', 'attribute2', 'attribute3'); // successful execution
const b = method(obj, 'attribute1', 'attribute3'); // compilation error: No overload matches this call

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

Having troubles with *ngFor in Angular 8? Learn how to use ng-template effectively

I need assistance creating a table with dynamically generated columns and using the PrimeNg library for the grid. Despite asking several questions, I have not received any responses. Can someone please help me achieve this? To generate table column heade ...

Having trouble with Angular routing in a .NET MVC 5 application with Angular 6?

I have integrated an Angular 6 application into an existing .NET MVC 5 application. A fallback route was set up in the MVC app (RouteConfig.cs) to direct "unknown" routes to the Angular app's router module (app.routes.ts). However, it seems that the r ...

Adding ngrx action class to reducer registration

Looking to transition my ngrx actions from createAction to a class-based approach, but encountering an error in the declaration of the action within the associated reducer: export enum ActionTypes { LOAD_PRODUCTS_FROM_API = '[Products] Load Products ...

Error Message: An issue has occurred with the server. The resolver function is not working properly in conjunction with the next

https://i.stack.imgur.com/9vt70.jpg Encountering an error when trying to access my login page. Using the t3 stack with next auth and here is my [...nextauth].ts file export const authOptions: NextAuthOptions = { // Include user.id on session callbacks ...

Guide to accessing a nested and potentially optional object property with a default value and specifying its data type

Just a simple query here... my goal is to extract data.user.roles, but there's a possibility that data may be empty. In such cases, I want an empty array as the output. Additionally, I need to specify the type of user - which in this instance is any. ...

Vuefire encountering an issue with Vue 3 and throwing a Vue.use error

After setting up a Vue app and importing Vue from the vue module, I encountered an issue: ERROR in src/main.ts:4:5 TS2339: Property 'use' does not exist on type 'typeof import("/data/data/com.termux/files/home/ishankbg.tech/node_modules/vue/ ...

What is preventing me from setting the User object to null in my Angular application?

Currently, I am working on a project in Angular and encountering a specific issue. In my service class, the structure looks like this: export class AuthService { authchange: new Subject<boolean>(); private user: User; registerUser(authD ...

Exploring FileReader in conjunction with React and Typescript

I am facing an issue while trying to upload a JSON file using an input element of type file. When I attempt to use the onload method on FileReader in TypeScript, I receive an error message saying "Cannot invoke an object which is possibly 'null'. ...

I'm looking to find the Angular version of "event.target.value" - can you help me out?

https://stackblitz.com/edit/angular-ivy-s2ujmr?file=src/app/pages/home/home.component.html I am currently working on getting the dropdown menu to function properly for filtering the flags displayed below it. My initial thought was to replicate the search ...

Inquiry regarding the return value of 'async-lock' in nodejs

I am utilizing the async-lock module in my typescript project to handle concurrency. However, I am encountering difficulties with returning the result within lock.acquire(...) {...}. Any guidance on how to resolve this issue would be greatly appreciated. ...

The issue of HTTP parameters not being appended to the GET request was discovered

app.module.ts getHttpParams = () => { const httpParamsInstance = new HttpParams(); console.log(this.userForm.controls) Object.keys(this.userForm.controls).forEach(key => { console.log(this.userForm.get(key).value) const v ...

Using @HostBinding based on the @Input() condition

I'm attempting to link the CSS class foo to my parent component by utilizing @HostBinding based on a condition I evaluate against a dynamic variable. However, I am struggling to get it to function as expected. Here are the different approaches I hav ...

What is the best way to extract data from a proxy in VUE3?

Currently, I am utilizing the ref() function to store data retrieved from Firebase. However, when attempting to filter and retrieve a single record, the outcome is not as expected. Instead of returning a single object, something different is being displaye ...

Ways to address observables in Angular in a manner similar to deferred objects

Transitioning from AngularJS to Angular has posed a challenge for me, especially when it comes to moving from promises to observables. Below is an example of my code in AngularJS: var deferred = $q.defer(), frame = document.createElement('newFrame ...

Angular: Exploring the Dynamic Loading of a Component from a String Declaration

Is there a way to compile a component defined by a string and have it render in a template while still being able to bind the click event handler? I attempted to use DomSanitizer: this.sanitizer.bypassSecurityTrustHtml(parsedLinksString); However, this a ...

Preventing text from wrapping in a TypeScript-generated form: Tips and tricks

I’m currently working on a ReactJS project and my objective is simple: I want all three <FormItem> components to be displayed in a single line without wrapping. However, I am facing the following output: https://i.stack.imgur.com/mxiIE.png Within ...

TypeScript is throwing an error because a value has been declared but never actually used in the

private tree_widget: ITreeWidget; private $ghost: JQuery | null; private drag_element: DragElement | null; private previous_ghost: IDropHint | null; private open_folder_timer: number | null; constructor(tree_widget: ITreeWidget) { this.tree_widget = t ...

What steps are necessary to ensure that the extended attribute becomes mandatory?

Utilizing Express, I have set specific fields on the request object to leverage a TypeScript feature. To achieve this, I created a custom interface that extends Express's Request and includes the additional fields. These fields are initialized at the ...

Guide to creating content on an NFC tag with Ionic

I am struggling with my button calling the test2 function and the code I have is not working as expected. Here is what I currently have: import { Component } from '@angular/core'; import { NFC, Ndef } from '@ionic-native/nfc/ngx'; @Com ...

Issue encountered while attempting to package Azure project in Visual Studio 2015 Update1 due to difficulty copying Typescript files

Since upgrading to VS 2015 Update 1 (that includes Typescript 1.7) and Azure SDK 2.8, packaging my Azure application for deployment has become a challenge due to an error in the file path where the packager is attempting to copy the js output file: Erro ...