How can I dynamically incorporate type-safe methods into a TypeScript class through programming?

Currently, I am developing a client for a web service that has 20 possible parameters (with the potential to increase). Each parameter corresponds to a specific method that needs to be exposed on the client. My current approach is as follows:

// keys.ts
export const FUNCTION_ONE = 'FunctionOne';
export const FUNCTION_TWO = 'FunctionTwo';
...
export const FUNCTION_TWENTY = 'FunctionTwenty';

// client.ts
import * as keys from './keys';
import { camelcase } from 'lodash';

export default class Client {
    makeCall(method: string): Promise<void> {
        // some implementation
    }
}

Object.values(keys).forEach((key) => {
    (Client.prototype as any)[camelcase(key)] = function () {
        return this.makeCall(key);
    };
});

The issue arises where Typescript is unaware of these dynamically added methods. To address this, I am exploring modifying the keys by introducing a typable camelcase form of each key. This trade-off would require manual typing but would streamline adding new methods to the keys.ts file.

My idea involves creating a type that combines the keys with the class to generate an interface containing all method names. A preliminary attempt looks like this:

// keys.ts
function key<T extends string>(command: string, name: T) {
    return { command, name };
}

export const FUNCTION_ONE = key('FunctionOne', 'functionOne');
...

// client.ts
export default class Client {
    // same as before
}

interface ClientInterface<T extends Record<string, { name: string }>> {
    // implementation??
}

export default type Client = ClientInterface<keys>;

How can I construct the ClientInterface type to automatically generate an interface with all method names from the keys file? Alternatively, is there a more efficient approach to achieve this goal?

Answer №1

If you prefer, you can consider using the following code snippet:

type ClientInterface<T extends Record<string, { name: string }>> =
    {[N in T[keyof T]["name"]]: () => Promise<void>};
export type ClientType = ClientInterface<typeof keys>;

Alternatively, if you are open to naming your constants in camel case, the implementation becomes slightly more straightforward. This way, the "rename" command can efficiently update all references:

type ClientInterface<T> =
    {[N in keyof T]: () => Promise<void>; };
export type ClientType = ClientInterface<typeof keys>;

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

Update the datalist in the view once the user has completed typing in the textbox using Angular 7

Struggling to automatically refresh a datalist in the view once the user finishes typing in the textbox and updates the results. I've experimented with angular directives, Observable, timeouts, and debounces without success. It seems like I've ex ...

Visual Studio Code continues to compile code automatically without requiring me to save any changes

Question: VSC triggers compilation even without any file changes in Angular(6) project ng serve It's frustrating when Visual Studio Code starts compiling repeatedly, even when no changes have been made. How can I prevent this from happening? I&apos ...

The Typescript error message states that the type '{ onClick: () => void; }' cannot be assigned to the type 'IntrinsicAttributes'

I'm a beginner in Typescript and I'm encountering difficulties comprehending why my code isn't functioning properly. My goal is to create a carousel image gallery using React and Typescript. However, I'm facing issues when attempting t ...

Understanding the expiration date of a product

I'm seeking assistance with identifying expiration dates from a list of data. For instance, if I have a list containing various dates and there is an expireDate: 2019/11/20, how can I highlight that date when it is 7 days away? Here's an example ...

Merge two RxJS Observables/Subscriptions and loop through their data

Working with Angular (Version 7) and RxJS, I am making two API calls which return observables that I subscribe to. Now, the challenge is combining these subscriptions as the data from both observables are interdependent. This is necessary to implement cer ...

Change typescript so that it shows "require" instead of "import" when outputting

Currently, I am converting TypeScript code to JavaScript for an application that is specifically targeting Node v.14. My goal is to have the output contain require statements instead of import statements. This is what my configuration file looks like: { ...

Unable to alter fxFlex property within Component using "setAttribute('fxFlex', '25%')" does not function properly in Angular 6

Currently, I am utilizing the flexLayout module to create responsive divs in my Angular application. You can find more information about flexLayout at https://github.com/angular/flex-layout and also at https://alligator.io/angular/flex-layout/. const nav ...

While validating in my Angular application, I encountered an error stating that no index signature with a parameter of type 'string' was found on type 'AbstractControl[]'

While trying to validate my Angular application, I encountered the following error: src/app/register/register.component.ts:45:39 - error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used ...

What is the best way to delay a recursive JavaScript function for 3 seconds?

Before writing this post, I have already come across the following questions: how-to-pause-a-settimeout-call how-to-pause-a-settimeout-function how-to-pause-a-function-in-javascript delay-running-a-function-for-3-seconds Question The below code snipp ...

A Guide to Retrieving Parameters and Request Body using Express and Typescript

When I use the PUT method, I encounter this issue: const createFaceList = (req: Request<{faceListId : string}>, res: Response, next: NextFunction) => { console.log(req.body.name); console.log("faceListID = " + req.params.faceListId); a ...

Dispersed data points within ng2-charts

After reviewing the ng2-charts documentation at , unfortunately, I did not come across any information regarding a Scattered Plot. Are there alternative methods to create a Scattered plot chart in ng2-charts? Any helpful tricks or customization options a ...

Angular 14: A collection and schematic must be provided for execution to proceed with the process

I've recently started learning angular. After installing the latest version, I created an app called "test" using the command ng new test. Next, I opened the app in Visual Studio Code and tried to create a new component by entering the command: ng g ...

Regular Expressions: Strategies for ensuring a secure password that meets specific criteria

Struggling to craft a regex for Angular Validators pattern on a password field with specific criteria: Minimum of 2 uppercase letters Minimum of 2 digits At least 1 special character. Currently able to validate each requirement individually (1 uppercase ...

What is the most effective approach to managing permissions in Angular 4?

For my Angular 4 project, I am looking to implement a permission system that will retrieve permissions from an API in the form of id arrays. Certain entities such as users or blog posts will have properties specifying allowed actions like editing or deleti ...

Issue with data-* attributes in MaterialUI React component causing TypeScript error

I am encountering an issue while attempting to assign a data-testid attribute to a Material-UI Select component. The Typescript error I am facing is as follows: Type '{ "data-testid": string; }' is not compatible with type 'HTMLAttributes&a ...

Conditional Return Types in a Typescript Function

There is a function that can return two different types, as shown below: function doSomething(obj: {a: string, b?: string}): string | number { if (obj.b) { return 'something' } return 1 } When the function is called with an object cont ...

Indeed, the Typescript Schema Interface for `Yup.lazy()` is essential for creating dynamic validation schemas

I am currently facing a challenge in creating a TypeScript interface for a Yup schema (https://github.com/jquense/yup/blob/master/docs/typescript.md) Here is the TypeScript interface I am working with: interface TestInterface { name: string; }; The fol ...

React animation failing to render underline animation

After tinkering with the underline animation while scrolling down on Codepen using Javascript, I successfully implemented it. You can check out the working version on Codepen. This animation utilizes Intersection Observer and a generated svg for the underl ...

Developing a custom data structure that identifies the keys of an object with a designated nested attribute

There is an intriguing scenario where dynamically defining types from a centralized JSON data store would prove extremely beneficial. Allow me to elaborate. The JSON file I possess contains a roster of various brands. // brands.json { "Airbus": { ...

When attempting to run npm run build for a Next.js application on an EC2 instance, it unexpectedly terminates on its own

While attempting to deploy my Next.js app on EC2, I encountered an issue where the npm run build command was being automatically killed. Suspecting it may be due to insufficient RAM, I switched to an instance type with 4GB of RAM (t3.medium), but the probl ...