What are the best methods for utilizing generics and mixins to expand a class in Angular?

Context

I am aiming to construct a structure that consists of both abstract (represented in blue) and concrete elements (depicted in green):

https://i.sstatic.net/zBUAO.png

To achieve this, I referred to this particular solution which led me to develop the following code:

export abstract class BaseResourceModel {
    public id?: string;
}

type ConcreteClass<C> = new (...args: any[]) => C;

export interface IBaseList<T extends BaseResourceModel>
  extends ConcreteClass<BaseList<T>> {
}

export function TableMixin<T extends BaseResourceModel>() {
  return function <B extends IBaseList<T>>(Base: B){
    class Temporary extends Base implements OnInit {
       (...)
    }
    return Temporary;
  };
}

@Component({ template: '' })
export abstract class BaseList<T extends BaseResourceModel>
  implements OnInit, OnDestroy {
  (...) 
}

@Component({ template: '' })
export abstract class BaseServerList<T extends BaseResourceModel> extends BaseList<T> {
  (...) 
}

Issue

I am unsure how to properly create the abstract BaseServerTable, extending from BaseServerList and using TableMixin while retaining the generic

<T extends BaseResourceModel>
. The attempts I made resulted in errors as shown below:

  • First attempt:
class DummyBaseServerList<T> extends BaseServerList<T> { }

@Component({ template: '' })
class BaseServerTable<T> extends TableMixin<BaseResourceModel, typeof DummyBaseServerList>()(BaseServerList<T>) {
  (...)
}

Produces the error:

Expected 1 type arguments, but got 2.ts(2558)

  • Second attempt:
class DummyBaseServerList<T> extends BaseServerList<T> { }

@Component({ template: '' })
class BaseServerTable extends TableMixin<BaseResourceModel>()(DummyBaseServerList) {
  (...)
}

Results in:

No base constructor has the specified number of type arguments.ts(2508)

  • Third attempt:
@Component({ template: '' })
class BaseServerTable<T> extends TableMixin<BaseResourceModel>()(BaseServerList<T>) {
  (...)
}

Leads to:

Value of type 'typeof BaseServerList' is not callable. Did you mean to include 'new'?ts(2348)

Inquiry

How can I effectively implement this pattern? What am I overlooking? Despite thorough research, I have yet to find suitable examples or guidance for a scenario like this.

Answer №1

To achieve the desired syntax, consider using the following code snippet:

class BaseServerTable<T> extends
    (TableMixin<BaseResourceModel>()(DummyBaseList))<T> { } // correct implementation

The function TableMixin has one type parameter and no function parameters, so ensure to call it as

TableMixin<BaseResourceModel>()
. This will result in the following output:

const TMBRM = TableMixin<BaseResourceModel>();
/* const TMBRM: <B extends IBaseList<BaseResourceModel>>(Base: B) => {
    new (...args: any[]): TableMixin<BaseResourceModel>.(Anonymous function)<B>.Temporary;
    prototype: TableMixin<...>.(Anonymous function)<...>.Temporary;
} & B */

This generates a function that takes a class constructor and returns another class constructor. You can then pass DummyBaseList as an argument like this:

const TMBRMDBL = TMBRM(DummyBaseList);
/*const TMBRMDBL: {
    new (...args: any[]): TableMixin<BaseResourceModel>.
       (Anonymous function)<typeof DummyBaseList>.Temporary;
    prototype: TableMixin<...>.(Anonymous function)<...>.Temporary;
} & typeof DummyBaseList */

Now, you have a class constructor. It appears to be generic because it can also be assigned the type typeof DummyBaseList, allowing for extension with a type parameter:

class BST<T> extends TMBRMDBL<T> { } // correct syntax

Extending a generic class in TypeScript follows specific rules, and while normally applying a generic type parameter to a generic class constructor is not allowed, there are exceptions when extending a generic class.


This should resolve any syntax errors in your code. However, keep in mind that due to the specialized angular-specific functionality, the behavior of this setup may vary. Proceed with caution and test thoroughly before implementing further changes.

Click here for Playground link to view code

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

Issue with subscribing to a shared service in Angular 2

I've encountered some challenges with BehaviorSubject while using a shared service across three components: import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; @Injectable() export ...

Using an additional router-outlet in an Angular 2 application: a step-by-step guide

I have been facing an issue with my angular2 app where I am attempting to use 2 router-outlets. Despite reading numerous blogs and conducting multiple Google searches, I have not been able to make it work. I would greatly appreciate any suggestions on why ...

Is there a way to create an interpolated string using a negative lookahead condition?

When analyzing my code for imports, I will specifically be searching for imports that do not end with -v3. Here are some examples: @ui/components <- this will match @ui/components/forms/field <- this will match @ui/components-v3 ...

Eliminate items from an array using TypeScript

What is the procedure for removing an object from an array in typescript? "revenues":[ { "drug_id":"20", "quantity":10 }, { "drug_id":"30", "quantity":1 }] I am looking to delete the drug_id from all objects. Can you p ...

What steps should I take to prevent inadvertently altering a different text box while utilizing two-way binding for quantity?

Currently, I am using *ngFor within my mat-card to display data from my book table. I have also included a textbox on each card for users to input the quantity they wish to purchase. To achieve this, I utilized two-way binding on the textboxes. However, I ...

How to access objects in Angular2 without using pipe or ngFor

Suppose I have the following data in an asymmetric array: [{'name':'user','password':'123'},{'title':'officer','grade':'5','age':'5'}] which is obtained f ...

Exploring the power of Angular's ViewChildren to iterate through QueryList items

My ngFor loop includes child components: @ViewChildren(SingleMultiSelectionComponent) singleMultiSelections: QueryList<SingleMultiSelectionComponent>; I am attempting to iterate through this QueryList: console.log(this.singleMultiSelections); // 1 i ...

Tips for incorporating flow and TypeScript typings into an NPM module

Are there any resources available for adding both flow and typescript typings to an NPM module at the same time? I've been struggling to find a comprehensive guide on this topic, and it seems to be a common issue faced by open source library maintain ...

What is the best way to exempt a unique situation from a directive's operation?

While troubleshooting a bug related to search functionality on my page, I encountered an issue with the search component. The search feature is working correctly and returning the expected values. However, when I clear the search criteria, I noticed that t ...

Encountering a red squiggly line while working on an Angular 2 project in an HTML file within Visual Studio

Whenever I incorporate Angular 2 code into my HTML files, I notice red squiggly lines appearing like this: https://i.sstatic.net/fhXGu.png Hovering over the red squiggly lines does not reveal any errors or warnings. It may be worth mentioning that I am ...

Tips for bundling TypeScript code with both personal and external modules included

I am working on a typescript project and I need to include an external npm library in my code. How can I combine all the files into a single javascript file that includes both my code and the library it relies on? Essentially: file1.ts (requires file2.t ...

How do I determine which radio button is currently selected within my Angular2 application?

In my HTML, I have the following radio buttons: <div class="col-lg-4" id="radioButtons"> <form action=""> <fieldset id="capacity"> <legend>capacity</legend> <label for="input-ao">< ...

Difficulty encountered when transferring characteristics of a subcategory to the chief category constructor within Angular

I have a requirement to pass the 'name' variable from the Derived component to the constructor of the Base Component. The purpose is to enforce that Derived components extending from the Base components must provide the 'name' variable. ...

There is no equality between two required types that are equivalent

When trying to assign Required<T> (where T extends A) to Required<A>, the operation fails. Consider this simplified example: type A = { a?: number }; type B<T extends Required<A>> = T; type C<T extends A> { b: B<Requir ...

Applying a consistent Selection Filter across multiple routes using identical data but varying selections

Included in the main screen are Selection Filters, which consist of 3 levels: Country, Cities, and Recreations. These filters need to be consistent across all routes, with "select all" at all levels upon initial load. However, a new route has been introd ...

What is the method for updating a property in an object of a Typescript class?

I am trying to work with a Fruit class: export class Fruit { constructor(public id: number, public name: string) {} public changeName(_name: string): void { console.log('changing name') this.name = _name } } My imple ...

Tips for exporting and reusing third-party types in TypeScript

I am facing a challenge with my package, which relies on a 3rd party package API for most of its functions. How can I export the types from the 3rd party package API in my own package? For instance: React uses @types/react to define its types Let's ...

An unexpected error causes the entire application to come to a halt, specifically due to a property being undefined. Assistance is

Issue Reproduction: 1. We have a list of advertisers (our clients) for whom we run various marketing campaigns. 2. Upon clicking the "Campaign" button for a specific advertiser. Result: You are taken to the "campaigns" page displaying all campaigns for ...

What could be causing the mat-radio button in one card to toggle all other cards as well?

Within my home.component.html file, you will find the following code: <div class="grid grid-cols-5 gap-4 pt-10"> <div *ngFor="let card of cards" class=""> <div *ngIf="card==null;then nil else notnil&q ...

Generating TypeScript user-defined type guards from interfaces programmatically

Within my Angular2 project, I have defined an Interface called GridMetadata: grid-metadata.ts export interface GridMetadata { activity: string; createdAt: object; totalReps: number; updatedAt: object; } In my Service, there is a public method na ...