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

Conditional application of Angular animations is possible

After implementing the fadein effect from Angular-Animations in my ASP.NET based Angular project, I encountered an issue where only the first row is faded-in while the other rows are not displayed when using *ngIf. Here is a snippet of the code: <ng-te ...

Is it possible to concurrently hot module reload both the server (.NET Core) and client (Angular)?

Using the command 'dotnet watch run' to monitor changes in server code and 'ng build --watch' for Angular code updates has been successful. It rebuilds the code correctly into directories "bin/" and "wwwroot/" respectively. myapp.cspro ...

In Vue3, when using the `script setup` with the `withDefaults` option for a nested object, its attributes are marked as required. How can this issue

I have defined a props object with certain attributes: interface Props { formList: BaseSearchFormListItemType[], inline?: boolean searchBtn?: { show?: boolean text?: string type?: string size?: string } } const props = withDefaults( ...

I am facing the dilemma of having an identical button appearing in two separate locations. How can I determine which button has been clicked?

I am currently using ng2-smart-table and have implemented a custom filter with the same button in both filters. However, I am unsure of how to determine which button is being clicked. https://i.stack.imgur.com/b1Uca.png Below is the component code for th ...

Dependency mismatch in main package.json and sub package.json

Imagine you have a project structure in Typescript set up as follows: root/ api/ package.json web/ package.json ... package.json In the main package.json file located in the root directory, Typescript is installed as a dependency to make ...

Error Message: The Query<DocumentData> type cannot be assigned to the DocumentReference<DocumentData> parameter

Currently, I am attempting to execute a query against Firestore data. Here is my code snippet: import { collection, getDoc, query, where } from "firebase/firestore"; import { db } from "../../utils/firebaseConfig"; const getQuery = a ...

Provide a parameter for a function's callback

I am attempting to utilize lodash's debounce function to delay the onChange event. See the code snippet below. import React, { useState, useEffect, useCallback } from "react"; import { TopBar } from "@shopify/polaris"; import { debounce } from "lodas ...

Creating a cohesive "notification" feature in React with TypeScript that is integrated with one specific component

I am currently working on designing my application to streamline all notifications through a single "snackbar" style component (utilizing the material UI snackbar component) that encompasses the entire app: For instance class App extends React.Component ...

Unable to exclude folder while creating production build is not functioning as intended

I've got a directory full of simulated data in the "src/api/mock" folder, complete with ts and JSON files. I'm attempting to have Webpack skip over them during the production build process. I attempted to implement the following rule, but unfortu ...

Enhancing TypeScript Native Interface Properties in TypeScript 2.4

After discovering an error in the native Typescript interface for HTMLTextAreaElement, I realized the need to make a correction. The current interface looks like this: interface HTMLTextAreaElement { setSelectionRange(start: number, end: number): void; ...

Received a 'Vue error: Redundant navigation to current location prevented' when attempting to refresh the page with updated parameters

I'm attempting to refresh the page with new parameters when the state changes: @Watch('workspace') onWorkspaceChanged(o: Workspace, n: Workspace){ if(o.type === n.type && o.id === n.id) return; this.$router.push({name: this.$rout ...

Issue with updating Angular list reference when deleting an item

My current task involves implementing a feature that displays selected items from a hierarchical structure on the right side. slice.component.ts : import { Component, Input, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core&a ...

The value produced by the interval in Angular is not being displayed in the browser using double curly braces

I am attempting to display the changing value on the web page every second, but for some reason {{}} is not functioning correctly. However, when I use console.log, it does show the changing value. Here is an excerpt from my .ts code: randomValue: number; ...

Show the value in Angular in a dynamic way

My template needs to display the text 'Passed' only if item.name === 'michael' is not true. The component receives data in an array called courses[] from its parent. There are two interfaces, Courses and Teachers, where each course ID h ...

Struggling with inter-component communication in Angular without causing memory leaks

After researching different methods, it appears that the recommended way for unrelated Angular components to communicate is by creating a service and utilizing an RxJS BehaviorSubject. A helpful resource I came across outlining this approach can be found h ...

Exploring the world of third-party APIs

I am currently working on fetching data from an external API and displaying it. In order to enhance flexibility, I am aiming to completely separate the API integration from my code and use custom-defined data structures instead. Here is a brief visual ov ...

How can contextual binding be implemented in TypeScript?

In Laravel, you have the option to specify which class should be imported if a particular class is accessed. For example, if someone tries to use the Filesystem contract, it will return the Storage Facade (Laravel Contextual Binding). Similarly, if someo ...

What could be causing the error I encounter when attempting to send JSON in a GET request?

Currently, I am utilizing Angular 10 in my project. I am attempting to send an object in a GET request, so I decided to convert it to JSON: getData(dataPlan: Data): Observable<Response<InfoType[]>> { return this.client.get<Response< ...

Leveraging Shared Modules Component across multiple modules in Angular can enhance code re

In my project structure, I have a shared folder containing shared.module.ts. Additionally, there is a modules folder with sub-modules, one of which is Dashboard.module.ts. Inside the shared module, I created a custom sidebar menu that I intend to use withi ...

The implementation of TypeScript 3.5 resulted in a malfunction where the imported namespace was unable to locate the Enum during runtime

I recently upgraded an older Angular.js application from Typescript 2.7 to 3.5 and successfully compiled it using tsc.exe. During application runtime, I encountered an error message in certain parts of the code: TypeError: Cannot read property 'Enu ...