TypeScript: Extending a Generic Type with a Class

Although it may seem generic, I am eager to create a class that inherits all props and prototypes from a generic type in this way:

class CustomExtend<T> extends T {
    constructor(data: T) {
        // finding a workaround to distinguish these two classes
        const prototype = { ...CustomExtend.prototype };
        Object.assign(prototype, Object.getPrototypeOf(data));
        Object.setPrototypeOf(this, prototype);
        Object.assign(this, data);
    }

    CustomMethod() { }
}

From now on, I would be able to create an instance of the CustomExtend class and utilize the types of both classes like so:

const obj = new CustomExtend<Model>(data);
obj.CustomMethod(); // works fine
obj.ModelMethod(); // excellent!

One solution could be using intersection, similar to this:

const obj: CustomExtend & Model = new CustomExtend(data) as any;

Although this method worked, I feel there might be a better alternative. Any suggestions?

Answer №1

In TypeScript, it is not possible to implement or extend another type T unless all the keys of T are known at compile time. This limitation prevents the creation of a class like

GenericExtend<T> implements T {...}
.

To achieve similar behavior, you can use an intersection and confine the type assertion to the constructor function. Here's how you can refactor the code:

class _GenericExtend<T> {
  constructor(data: T) {
    const proto = { ..._GenericExtend.prototype };
    Object.assign(proto, Object.getPrototypeOf(data));
    Object.setPrototypeOf(this, proto);
    Object.assign(this, data);
  }
  GenericMethod() { }
}

Redefine GenericExtend as both a type and a constructor with the desired intersection behavior:

type GenericExtend<T> = _GenericExtend<T> & T;
const GenericExtend: new <T>(data: T) => GenericExtend<T> = _GenericExtend as any;

The final as any is necessary for type assertion. After making these changes, you should be able to achieve the behavior you desire:

interface Model {
  ModelMethod(): void;
}
declare const data: Model;

const obj = new GenericExtend(data);
obj.GenericMethod(); // works fine
obj.ModelMethod(); // also works fine

For further reference and testing, you can check out the code on the TypeScript playground by clicking here.

Answer №2

When facing a similar requirement, I found a way to make it work without the need for strict intersection (by creating a dedicated type for type checking). Here is how I achieved it:

class A {
  a() {
    ...
  }
}

class B {
  b() {
    ...
  }
}

type Constructor = new (...args: any[]) => any

function genericExtend<T extends Constructor>(target: T) {
  return class GenericExtended extends target {
    constructor(...args: any[]) {
      super(...args)
    }

    genericMethod() {
      ...
    }
  }
}

const instanceOfA: GenericExtended & A = new (genericExtend(A))()
const instanceOfB = new (genericExtend(B))()

instanceOfA.a() // ok with type checking
instanceOfA.genericMethod() // ok with type checking

instanceOfB.b() // ok without type checking
instanceOfB.genericMethod() // ok without type checking

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

The feature of "compile on save" is not functioning properly in my current Angular project

Yesterday I used the angular cli (ng new my-app) to create a new project, but unfortunately the "compile on save" option is not functioning properly. Interestingly, I have two projects on my computer and this feature works fine for one of them but not for ...

Utilize Angular's Reactive Form feature to track changes in Form Control instances within a Form Array and calculate the total value dynamically

I am currently utilizing Angular Reactive Forms to loop through an array of values and I want to include a total field after the Form Array that automatically updates whenever there are changes in the Form Array control values. Here is some sample data: ...

How can I display and link a base64 string to an Image as a source in Nativescript?

I'm having trouble displaying and binding a base64 image as an ImageSource in my View. The image doesn't show up at all, and I couldn't find any helpful information about it in the documentation. Am I missing something? The imageSource prop ...

Implementing React custom component with conditional typing

My goal is to enable other developers to set a click handler for a button only if the button's type is set to button. Users can only set the type to either button or submit. I want to restrict developers from setting the onClick property on the comp ...

Revise the observable to trigger a NgbModal from a service

I have a situation in my Angular 11 service where I am using a Ngbmodal component to subscribe when it is closed. Below is the code snippet: showMessage(messageData: MessageDataDTO): Observable<MessageResult> { return new Observable((result) =&g ...

Class for Eliminating the Background Image Using Bootstrap

Is there a Bootstrap class that can be used to remove a background image from a div? Currently, I have this style defined in my CSS: background-image: linear-gradient(to bottom, rgba(0,0,0,0.1), rgba(0,0,0,0)); I would like to remove it using: bg-img-non ...

Experiencing issues while trying to render a component with dynamic content in Next.js

Currently, I am facing an issue while trying to display Leaflet maps in Next.js with Typescript. I came across the suggestion to disable server-side rendering (ssr) to prevent the 'window not defined' error. However, when implementing the followi ...

Handling JSON Objects with Next.js and TypeScript

Currently, I am working on a personal project using Next.js and Typescript. Within the hello.ts file that is included with the app by default, I have added a JSON file. However, I am facing difficulties in mapping the JSON data and rendering its content. T ...

The Angular 2 Router's navigation functionality seems to be malfunctioning within a service

Currently, I am facing an issue with using Angular2 Router.navigate as it is not functioning as expected. import { Injectable } from '@angular/core'; import { Http, Headers } from '@angular/http'; import { Router } from '@angular/ ...

How can you indicate that a function in TypeScript is expected to throw an error?

An error occurs when trying to compile the following code: "a function whose declared type is neither void nor any must return a value or consist of a single throw statement." Is there a way to indicate to the compiler that _notImplemented throws an excep ...

An error was encountered in the rxjs-compat module at operator/shareReplay.d.ts line 2, character 10: TypeScript error TS2305

Currently, I am in the process of upgrading a basic Angular skeleton application from version 5 to version 6. However, I have encountered an issue while attempting to run the application: ERROR in node_modules/rxjs-compat/operator/shareReplay.d.ts(2,10): ...

Lazy-loading modules in SSR Angular 8 applications are currently unspecified

I am currently in the process of setting up my Angular 8 application to work with server-side rendering (SSR). However, I am encountering some undefined errors in webpack when running my application using ng serve, especially with lazy-loaded modules. Ever ...

Error: monaco has not been declared

My goal is to integrate the Microsoft Monaco editor with Angular 2. The approach I am taking involves checking for the presence of monaco before initializing it and creating an editor using monaco.editor.create(). However, despite loading the editor.main.j ...

What could be the reason for receiving an HttpErrorResponse when making a GET request that returns byte data

When using these headers, the API returns byte data as a response. let headers = { headers: new HttpHeaders({ 'Content-Type': 'application/octet-stream', 'responseType':'arraybuffer' as 'js ...

Trouble with querying NG elements using "queryAll(By.css)" in Angular and Jasmin unit testing

I've encountered an unusual problem that needs to be resolved for me to successfully complete a unit test for a project I'm currently engaged in. Here is what my unit test currently looks like: it('should display the navbar list', ...

Is there a way to navigate to a specific component selector within an ngFor loop?

I have a scenario where I have multiple components running inside *ngFor on the same page. My goal is to create button links at the top of the page that, when clicked, will scroll to the corresponding component on the page. Below are the code snippets tha ...

Combining the values of a particular key with duplicate objects into a single object within an array of JSON objects using Angular and Typescript

I'm currently facing a challenge in my Angular project where I have an array of JSON objects. These objects are very similar, differing only in one key-value pair. My goal is to combine these similar objects into one while appending the varying values ...

Experiencing the error message "delete(...).then(...).error is not a function" while attempting to remove a file from Firebase storage using AngularFire and TypeScript

I'm trying to implement the code snippet from Firebase documentation to delete a file and then upload a new image in the same directory on Firebase Storage. However, I keep encountering an error message saying "delete(...).then(...).error is not a fun ...

There is an issue with the property 'updateModf' in the constructor as it does not have an initializer and is not definitely assigned

When working with an angular reactive form, I encountered an issue. After declaring a variable with the type FormGroup like this: updateModf:FormGroup; , the IDE displayed an error message: Property 'updateModf' has no initializer and is not def ...

The Typescript SyntaxError occurs when attempting to use an import statement outside of a module, typically within a separate file that contains

I am currently developing a Minecraft bot using the mineflayer library from GitHub. To make my code more organized and reusable, I decided to switch to TypeScript and ensure readability in my project structure (see image here: https://i.stack.imgur.com/znX ...