How can TypeScript allow an argument to only accept keys that match another argument?

I'm currently developing a library that deals with linked lists. The current implementation is hardcoded to work with a list node type containing a "next" field that points to the next node of the same type. However, I am looking to make it more flexible and agnostic to the specific field names used for links. This means it should work whether the list nodes are connected by "prev" references, "next" references, "previous" references, "pointer" references, or even numerical indices (like when using two-element arrays to simulate cons cells). In this case, the user would pass in a string indicating the name of the list link field.

One approach is to allow the library to accept objects conforming to an interface with an index signature of type any (e.g.,

interface ListNode { [key: string]: any }
), trusting that the user will provide an object with the specified link key. However, I would prefer to validate that the list nodes being passed to the library actually contain the fields with the specified names. This aligns with the purpose of utilizing TypeScript.

My current attempt goes like this:

function doListStuff(head: { [ptr]: ??? }, ptr: string) { ... }

However, I'm struggling to determine what to replace ??? with. Is there a way to define recursive anonymous types?

I also experimented with the following:

interface ListNode<Ptr extends string> {
   [Ptr]: ListNode<Ptr> | null;
}

But this resulted in a series of errors:

[ts] A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type. [ts] Cannot find name 'Ptr'. [ts] A computed property name cannot reference a type parameter from its containing type.

So, my question is: is there a way to define an interface with computed property names, or any other method to achieve what I'm aiming for?

Answer №1

If you want to create an object in TypeScript with keys that are of a generic key-like type, you can use a mapped type. The structure will look similar to this:

type ListNode<P extends string> = { [K in P]: ListNode<P> | null };

Although the above definition may seem to lack data, you can easily include additional structure through intersection types or other similar techniques.

For example, you can implement a function like doListStuff() as shown below:

function doListStuff<P extends string>(head: ListNode<P>, ptr: P) {
    const next: ListNode<P> | null = head[ptr];
    console.log(JSON.stringify(next));
    if (next) doListStuff(next, ptr);
}

You can test its functionality with the following code snippet:

doListStuff({ foo: { foo: { foo: { foo: null } } } }, "foo");
//{"foo":{"foo":{"foo":null}}}
//{"foo":{"foo":null}}
//{"foo":null}
//null;

Hope this explanation helps! Feel free to explore the code further using this code 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

Tips on creating a hierarchical ul list from a one-dimensional array of objects

I have an array filled with various objects: const data = [ {id: "0"},{id: "1"},{id: "2"},{id: "00"},{id: "01"},{id: "02"},{id: "11"},{id: "20"},{id: "23"},{id: & ...

Running Typescript compilation in Azure DevOps

I'm in the process of setting up a pipeline to compile Typescript files (.ts) located within a specific folder. Thus far, I've successfully created a traditional pipeline that has installed npm. What steps should I take next? My assumption is tha ...

Implementing a dependent <select> within an HTML table is a useful feature to enhance user experience and organization of

I have set up a table with editable columns where values can be selected from a drop-down menu. I achieved this by using HTML select. The options in the 'Category tier 2' column are based on the selection made in the 'Category tier 1' c ...

Querying Cloud Firestore with User ID

I'm facing an issue with retrieving a subset of data based on the first letter of the name and including the UID of the document. No matter what I try, it just returns empty data. fetchDataByFirstLetter(firstLetter: string) { this.afs.collection(&a ...

Angular keeps FormArray elements' validity up-to-date as new elements are added to the array

I am facing an issue where I do not want the validators to run unnecessarily. Every element of FormArray is being validated asynchronously, so I prefer the validators to be triggered only when the control's value actually changes. It seems odd that va ...

encountering difficulties calling setAttribute within a function

I am encountering an issue while attempting to use setAttribute() within toggleDiv(). My IDE does not seem to recognize the function and is throwing an error. How can I resolve this problem so that the function is recognized? This specific case relates t ...

Is Angular Template Polymorphism Failing?

So I'm working with a base class that has another class extending it. export class Person { public name: string; public age: number; } export class Teacher extends Person { public yearsTeaching: number; } Now, in my component, I need to ...

What types should be used when passing a NgRx Action as a parameter to a function?

Within my Effects function, I have implemented the following code structure. I have included a few lines of code for the catchError block to ensure that: Any errors are handled by the state/store The errors are forwarded to the global error handler / Int ...

I am unable to transmit information using the `http.post` function

When attempting to send a post request to the backend, I am receiving a response code of 500 and an empty data object on the API side. Interestingly, using Postman gives successful results. return http.post(link,data,headers).subscribe(response=>{ ...

Creating JPEG images with specified dimensions. How can you add W x H sizing to an image?

I have been searching for a Deno/TypeScript code snippet that can create basic images with dimensions embedded on them. I have provided an example of the code below, which generates images in JPEG format, base64, and dataURL. The code works by adding RGB ...

Function Type Mapping

I am in the process of creating a function type that is based on an existing utility type defining a mapping between keys and types: type TypeMap = { a: A; b: B; } The goal is to construct a multi-signature function type where the key is used as a ...

Issue encountered with dynamic ngStyle variable: ERROR Error: Unable to locate a supporting object 'ngStyleSmall'

I have defined two variables for ngstyle ngStyleSmall = { width: '150px !important', 'max-width': '150px', }; ngStylemedium = { width: '250px !important', 'max-width&apo ...

Tips for creating a redirect to a specific page after clicking a link in an email using Angular

I've been working on implementing a feature in Angular where users can click on a link provided in an email and then get redirected to the respective page after authentication. I've tried a few different approaches, but none of them seem to be wo ...

Unlock the power of Env variables on both server and client components with Next.js! Learn how to seamlessly integrate these

In my Next.js app directory, I am facing the need to send emails using Nodemailer, which requires server-side components due to restrictions on client-side sending. Additionally, I am utilizing TypeScript in this project and encountering challenges when tr ...

tsconfig.json: No input files were detected in the configuration file

I am encountering an issue with my tsconfig.ts file where it says "No inputs were found in config file 'd:/self-study/Web/Learning/Express/tsconfig.json'. Specified 'include' paths were '["**/*"]' and 'exclude&a ...

Creating a OneToMany relationship in NestJS entity model

In my current project with NestJS, I am working on defining entity fields. While I have successfully defined a ManyToOne relation, I am facing difficulties in setting up the syntax for a OneToMany relation to match the structure of my other relationships. ...

Encountered a higher number of hooks rendered compared to the previous render error on a component without any conditional hook usage

Within my codebase, I have a component that is responsible for rendering a clickable link to initiate a file upload process. import { gql, useLazyQuery, useMutation } from '@apollo/client'; import { useEffect, useState } from 'react'; i ...

ngx-capture : Issue with capturing the entire page

Hey there! I'm currently using the ngx-capture package for capturing images, but I've encountered a problem. It seems to only capture the area that is visible in the browser. Is there a way to capture the whole page or an entire div? I came acr ...

There has been an unhandled runtime error: [object ProgressEvent] occurring with Next.js/Typescript

Exploring the world of nextJS and typescript is new to me. I am currently working on creating a simple blog using nextJS/typescript with a sanity CMS backend. Everything seems to be running smoothly during development, but then I encounter this Unhandled R ...

Error Encountered While Building AWS Amplify with Ionic 5 and Angular 10

Our team is currently facing a challenge at my company that we've been struggling to resolve, and I was hoping someone here could offer some assistance. We are using AWS Amplify in our Angular 10/Ionic 5 project, and encountering the following error: ...