Using Typescript generics within a callback function

I am currently working on developing a versatile service that can fetch data from a remote source and create objects based on that data.

@Injectable()
export class tService<T> {
    private _data: BehaviorSubject<T[]> = new BehaviorSubject([]);
    private url = '...url...';  // URL to web api

    constructor(private http: HttpClient) {    
        this.http.get<T[]>(this.url).subscribe(theThings => {
            theThings = _.map(theThings, (theThing) => new T(theThing));
            this._data.next(theThings);
        });
    }
}

Encountering an error

T only refers to type, but is being used as a value here
. I’m aware of what’s happening and have seen similar inquiries before.

For instance:

  • How to create a new object from type parameter in generic class in typescript?
  • TypeScript return object constructed form generic
  • Get constructor/instance from generic type in TypeScript

All solutions I’ve found either involve hardcoding the class at some point or integrating T's class constructor somewhere. However, I am stuck because the class that needs instantiation has parameters in its constructor, and I'm utilizing Angular's DI system, preventing me from adding parameters to the service's constructor.

constructor(private playersService: gService<Player>, private alliesService: gService<Ally>) { }

If anyone has any insights on how to proceed, it would be greatly appreciated. Ideally, I would prefer a solution that doesn't resort to a mere "hack". If the options are between something convoluted and duplicating the service with slight modifications, I’d opt for the latter.

Answer №1

When it comes to class names, it's important to note that they refer not only to the type of the class but also to its constructor. This is why you can use both let x: ClassName and new ClasName(). On the other hand, a generic parameter is solely a type, similar to an interface, which is why using it as a value causes the compiler to throw an error (since a constructor function is expected). To resolve this issue, you need to pass an additional parameter specifying the constructor for T:

export class tService<T> {
    private _data: BehaviorSubject<T[]> = new BehaviorSubject<T>([]);
    private url = '...url...';  // URL to web api

    constructor(private http: HttpClient, ctor: new (data: Partial<T>)=> T) {    
        this.http.get<T[]>(this.url).subscribe(theThings => {
            theThings = _.map(theThings, (theThing) => new ctor(theThing));
            this._data.next(theThings);
        });
    }
}
//Usage
class MyClass {
    constructor(p: Partial<MyClass>) {
        // ...
    }
}
new tService(http, MyClass)

Note The parameters to the constructor signature may vary according to your use case.

Edit

You mention that you can't add arguments to the constructor. Generics (and all types in general) are erased at runtime, making it impossible to rely on T at runtime. Thus, someone will need to pass in the class constructor. One approach is to create dedicated classes for each instance of T and inject the specific class:

class tServiceForMyClass extends tService<MyClass> {
    constructor(http: HttpClient)  {
        super(http, MyClass)
    }
} 

Alternatively, you could perform your operations based on the constructor by implementing an init method that takes the constructor as a parameter:

export class tService<T> {
    private _data: BehaviorSubject<T[]> = new BehaviorSubject<T>();
    private url = '...url...';  // URL to web api

    constructor(private http: HttpClient){}
    init(ctor: new (data: Partial<T>)=> T) {    
        this.http.get<T[]>(this.url).subscribe(theThings => {
            theThings = _.map(theThings, (theThing) => new ctor(theThing));
            this._data.next(theThings);
        });
    }
} 

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

I find it frustrating that Angular consistently redirects me to the login page every time I attempt to navigate to a different page

I currently have a website with both normal user-accessible pages and an admin dashboard accessible only by admins through ngx-admin. To restrict users from accessing the admin dashboard, I've implemented an auth guard that redirects them to the logi ...

The lazy-loaded Angular module encountered an issue when attempting to access its child routes

I have been working on a dashboard app and currently, I've implemented two lazy loaded modules named AuthModule and AdminModule. The routing configuration in my app-routing-module.ts is as follows: const routes: Routes = [ { path: '&apo ...

Tips for troubleshooting a React Native project built with Expo and utilizing TypeScript

I started a new Expo project with TypeScript integration. After launching the app using expo start, I noticed that the Chrome debugger only displays .js files at http://localhost:19001/debugger-ui/: https://i.stack.imgur.com/cmyy9.png How can I make sur ...

List of nested objects converted into a flat array of objects

Looking to transform a data structure from an array of objects containing objects to an objects in array setup using JavaScript/Typescript. Input: [ { "a": "Content A", "b": { "1": "Content ...

The overlay pop-up does not reposition on scroll movements

Within the image, there is a MatDialog Box positioned on top of another dialog box. Inside the MatDialog Box, there is a Component called MyNewDetailComponent. This component is accessed via Overlay with the scrollOptions set to reposition(). The issue ar ...

The Angular router-outlet is refusing to display any content

I am new to Angular and currently learning through a lecture with hands-on practice. I have written the code below as instructed by my teacher, but it's not displaying anything on the screen. Can someone please assist me? app.module.ts : @NgModule({ ...

Is there an automatic bottom padding feature?

Currently, I am facing a challenge in fitting the loader into the container without it being overridden by the browser. Using padding-bottom is not an ideal solution as it results in the loader appearing un-resized and unprofessional. Any suggestions or co ...

Adjust the design of your Angular 2/5 app based on configuration file dynamically

I'm currently exploring the concept of a flexible layout that can be customized by users. Here's how it works: By default, there is a set layout of elements on the page (such as inputs, tables, etc). The user has the ability to rearrange where ...

How to Invoke a TypeScript Function in Angular 2 Using jQuery

Using the Bootstrap-select dropdown in Angular 2 forms with jQuery, I am attempting to call a Typescript method called onDropDownChangeChange on the onchange event. However, it seems to not be functioning as expected. import { Component, OnInit, ViewChi ...

Error: Uncaught TypeError in AuthContext in Next.js 13.0.6 when using TypeScript and Firebase integration

I'm currently trying to display a page only if the user is present in my app. Right now, the app is pretty bare bones with just an AuthContext and this one page. I had it working in React, but ran into some issues when I switched it over to TS and Nex ...

What is the way to send custom properties to TypeScript in combination with StyledComponents?

Encountering an error while attempting to implement Styled Components 3 with TypeScript: TS2365: Operator '<' cannot be applied to types 'ThemedStyledFunction<{}, any, DetailedHTMLProps<TableHTMLAttributes<HTMLTableElement>, ...

Create a variable and save data into it

I successfully fetched data from Firebase but I am having trouble storing it in a declared variable. I need to store the data in a variable so that I can use it in another method. Any assistance would be greatly appreciated. Below are the code snippets I ...

Additional optional class name within a TypeScript component

I am utilizing class names to apply multiple classes to a React component. Within my button, I want the option to include a class to the component if desired, but it should be optional. Unfortunately, I encountered a type error as depicted in the image li ...

Swapping the content of the API response with Angular 11 in the HTML

When the output of row.remarks is 1, I want to display it as "passed" and when it's 0, I want it to be displayed as "fail" in the HTML while using mat-table. HTML <ng-container matColumnDef="remarks"> <th class="font& ...

A guide on streamlining the process of generating "this." variables within Angular

When it comes to working with Javascript, particularly Angular, I find myself using the this. syntax quite often. I'm curious to know if it's possible to concatenate variables in order to create a this. variable. For instance, is this achievab ...

Guide on obtaining the total value from a JSON Array in Angular 2 using TypeScript

I received a JSON response which includes carts values with different amounts. "carts": { "value": [ { "Amt": 40 }, { "Amt": 20.25 }, ...

Transferring information between two components remains intact even after refreshing the page

The component product-list is responsible for showcasing all the products available: <div *ngFor="let product of products"> <a routerLink="/products/{{ product.url }}" [state]="product">{{ product.name }}</a> <p>{{ product.url ...

Is it possible for an uninitialized field of a non-null literal string type to remain undefined even with strict null checks in

It seems that there might be a bug in Typescript regarding the behavior described below. I have submitted an issue on GitHub to address this problem, and you can find it at this link. The code example provided in that issue explains the situation more clea ...

Looking to retrieve the selected item from the list using console.log

Brief explanation: I am trying to retrieve the clicked item from a list using console.log(i.match). Please refer to my **home.html** code snippet below for how the list is dynamically generated. home.html <ion-card> <ion-card-content *ngFor="l ...

Tips for ensuring that the code inside a subscribe block completes before moving on to the next iteration within a forEach loop in Angular

Within the code snippet below, there exists a for loop where an API call is made. The intention is to have the 1st API call complete and execute all the subscribed code before moving on to the next iteration of the loop, triggering another API call. Curre ...