Inform TypeScript about functions that are dynamically defined

I am implementing a unique class structure as shown below:

class App<objectList extends {}> {
   private objects: Map<keyof objectList, any> = new Map();


   add<T extends keyof objectList>(name: T, type: objectList[T]) {
       this.objects.set(name, type);

       this['get' + name] = () => {
          return type;
       }

       return this;
   }
}

When creating a new instance of this class, I intend to include extra objects that can be retrieved later using the function getObjectType() on the instance.

For example:

const App = new App<{Test: string, Test2: number}>().add('Test', 'this is my test string').add('Test2', 5);

App.getTest(); // returns 'this is my test string'
App.getTest2(); // returns 5

The functionality works as intended. However, TypeScript raises an error about these functions not existing. Is there a way to strongly type a similar scenario?

UPDATE

Is it feasible to achieve the same functionality as the `add` function directly in the constructor?

class App<objectList extends {}> {
    constructor(initObjects: objectList) {
       /** iterate over the initObjects, create the functions **/
    }
}

const inst = new App<{Test: string, Test2: number}>({
   'Test': 'this is my test string',
   'Test2': 5
});

inst.getTest();

Answer №1

When dealing with type mutations in the compiler, we have to provide explicit information within the add() method to indicate how we want to handle the returned value of this. One way to approach this is as follows:

add<K extends Extract<keyof O, string | number>>(name: K, type: O[K]) {
    this.objects.set(name, type);

    (this as any)['get' + name] = () => {
        return type;
    }
    return this as this & Record<`get${K}`, () => O[K]>
}

By calling the add function with a specific name and its corresponding type, the return type will be an intersection type of both this and an object containing a key named `get${K}` which returns a value of type O[K]. This necessitates the use of a type assertion to specify the augmented type for the returned value of this.

Testing this approach:

const app = new App<{ Test: string, Test2: number }>().
    add('Test', 'this is my test string').
    add('Test2', 5);

app.getTest(); // no error
app.getTest2(); // no error

If bypassing the builder pattern and passing the entire O object into the App constructor is preferable, creating instances of the App class with dynamically determined keys based on O becomes necessary. However, describing such functionality directly using class or interface declarations proves challenging.

new <O>(initObjects: O) => App<O>

A construct signature defining the App constructor that takes an initObjects parameter of type O and returns a value of type

App<O></code can help achieve this behavior.</p>
<pre><code>type App<O> = {
    [K in keyof O as `get${Extract<K, string | number>}`]:
    () => O[K]
};

This mapped type remaps keys so that each key in O results in a corresponding key with "get" prepended to it in the resulting App<O> structure.

To implement the App constructor conforming to this shape, a type assertion is needed along with utilizing a class expression:

const App = class App {
    constructor(initObjects: any) {
        Object.keys(initObjects).forEach(
            k => (this as any)["get" + k] = () => initObjects[k]
        );
    }
} as new <O>(initObjects: O) => App<O>;

Testing the newly constructed instances:

const inst = new App({ Test: "this is my test string", Test2: 5 });

console.log(inst.getTest().toUpperCase());

Results look favorable!

Access the code on the TypeScript 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 for modifying a class to accept a range of parameters from diverse functions

In my current Typescript project, I have implemented a builder to create objects for various methods. I am striving to make the builder adaptable for all methods in order to streamline the process without creating additional classes. At present, I have tw ...

Effortless implementation of list loading with images and text in the Ionic 2 framework

Can someone provide guidance on creating a lazy loading list with both images and text? I understand that each image in the list will require a separate http request to download from the server. Should caching be implemented for these image downloads? Addi ...

Tips for verifying the results of a specific element on a webpage using Angular

Hello all, I am currently learning Angular and facing an issue related to components. I have successfully created a component named "test". When I execute the code, I get the desired output. However, if I remove the tag from app.component.html, I end up ...

There was a problem with the WebSocket handshake: the response header value for 'Sec-WebSocket-Protocol' did not match any of the values sent

I've encountered an issue with my React project that involves streaming live video through a WebSocket. Whenever the camera firmware is updated, I face an error in establishing the WebSocket connection. Here's how I initiate the WebSocket: wsRe ...

Customizing Material UI CSS in Angular: A Guide

Recently, while working with the Mat-grid-tile tag, I noticed that it utilizes a css class called .mat-grid-tile-content, which you can see below. The issue I am encountering is that the content within the mat-grid-tile tag appears centered, rather than f ...

Angular 5 error: Decorators do not support function expressions

I am struggling to compile my angular project using the command ng build --prod The issue arises in the IndexComponent : index.componenent.ts import { Component, OnInit } from '@angular/core'; import { indexTransition } from './index.anim ...

Even with manual installation, the npm package still encounters dependency errors

Having trouble implementing the Imgur package from NPM into my Angular web app. The installation and import seemed to go smoothly, but when initializing a variable with the package, I encounter compile errors pointing to missing dependencies like 'cry ...

Error Alert: "Invariant Violation" detected in a TypeScript program utilizing React

When attempting to implement the react-collapse component in my TypeScript application along with a custom d.ts file, I encountered the following error: Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a ...

strange complications with importing TypeScript

In my Typescript projects, I frequently use an npm module called common-types (repository: https://github.com/lifegadget/common-types). Recently, I added an enum for managing Firebase projects named FirebaseEvent. Here is how it is defined: export enum Fi ...

How can you create a type in Typescript that is composed of a specific property taken from another type?

I'm still in the process of understanding typed languages, but imagine I have the following scenario: export interface Person { id: number; name: string; } const persons: Array<Person> = [ { id: 1, name: 'foo', }, { ...

Do Angular 2 component getters get reevaluated with each update?

What advantages do getters offer compared to attributes initialized using ngOnInit? ...

The module "vue-final-modal" does not have an exported member named 'ModalsContainer'

"vue-final-modal": "^3.4.11" -> update to "vue-final-modal": "^4.4.5", "vue": "^3.3.4", using composition API, TypeScript, and Vite Operating System: Windows Upon running the build command, I ...

Local machine encountering Typescript error, while remote test server remains unaffected

I am currently exploring the Microsoft Fabric UI tools and encountering an error on my local machine when trying to use them. /Developer/React/TCV.ts/tcv/src/CategorySelection.tsx(94,9): Type '(filterText: string, currentPersonas: IPersonaProps[], lim ...

Struggling with TypeScript and JsObservable? Let us assist you!

Having previous experience with JSRender, JSViews, and JSObservables, I recently embarked on a new project using TypeScript. Unfortunately, I am struggling to understand how to properly utilize TypeScript in my project, especially when it comes to referenc ...

using lodash to convert objects into arrays while maintaining parent-child relationships

Is there a way to achieve this transformation in lodash or any other JavaScript/TypeScript method? var obj = { a: [ {id:1},{id:2},{id:3}] b: [ {id:4},{id:5},{id:6}] c: [ {id:7},{id:8},{id:9}] } // Need to transform into var arr = [ {title:a ...

What's the best way to process a string array using iteration in Typescript?

I have an array of colors: colors = ['red','blue','purple']; I would like to display the following message: colors in ('red','blue','purple') When I tried to achieve this using forEach metho ...

Why is the format incorrect when the Angular 7 (Change)-Function on Input of type Date isn't functioning?

I am facing an issue with updating the date using key input and assigning the selected date to a property of my object. Below is the code I'm currently working with: <input type="date" [value]="dateTime()" (change)="setDate($event)"/> The dat ...

There seems to be a syntax error in the regular expression used in Angular TypeScript

I've encountered an error and I'm struggling to identify the syntax issue. core.mjs:6495 ERROR SyntaxError: Invalid regular expression: /https://graph.microsoft.com/v1.0/communications/callRecords/getPstnCalls(fromDateTime=2020-01-30,toDateTime ...

Incorporate an external library

I am currently facing a challenge in my angular2 project where I need to import a 3rd party library. Here are the steps I have taken so far: ng new myproject npm install --save createjs-easeljs npm install @types/easeljs However, I am stuck at this poin ...

Display a nested component post initialization in Angular

<ng-container *ngIf="isTrue; else notTrue"> <app-child [property1]="value" [property2]="value" [property3]="value" (function1)="func($event)" ></app-child> </ng-container> <ng-t ...