Create an instance of a class from a group of subclasses, all the while retaining the ability to access static members in Types

I seem to have encountered a dilemma where I am looking to have both the static and abstract keywords used for a member of an abstract class in TypeScript, but it appears that this combination is not supported.

The nearest workaround I could come up with is shown below:


        abstract class A {
        
            s: string;
            n: number;
            static identifier: string;
        
            constructor(s: string, n: number) {
                this.s = s;
                this.n = n;
            }
        
        }
        
        class B extends A {
        
            static identifier: string = "B";
        
        }
        
        class C extends A {
        
            static identifier: string = "C";
        
        }
        
        function identifyAndInstantiate(identifier: string, classes: (typeof A)[]) {
        
            var identifiedClass = classes.find((classCandidateConstructor: typeof A) => { return identifier == classCandidateConstructor.identifier });
            if (!identifiedClass) return;
            
            //error here: Cannot create an instance of an abstract class.(2511)
            var instance = new identifiedClass("foo", 12);
        
        }
        
        var classes: (typeof A)[] = [
            B,
            C
        

I've experimented with different collection types to solve this issue. The most successful approach I found was using a Map from the static member type to instances of the class, but it required manually mapping the values to their respective classes.

Another tactic I attempted was utilizing a Newable<T> type that defines its constructor, but this resulted in losing access to the subclass' static properties (as did other methods that exposed the constructor).

Is there a way to access both a subclass' static properties and its constructor after retrieving it from a collection of classes?

Answer №1

Explanation

To differentiate between subclasses (such as B, C) within a collection, an abstract property is defined in the base abstract class (A) and implemented in each concrete subclass. This allows for unique identifiers for each subclass.

Simple Code Example

abstract class A {
  static id: string;

  abstract someMethod(): void;
}

class B extends A {
  static id = 'B';

  someMethod(): void {
    console.log('Method in B');
  }
}

class C extends A {
  static id = 'C';

  someMethod(): void {
    console.log('Method in C');
  }
}

function bar(subclassId: string, subclassesOfA: (typeof A)[]): void {
  let subclass = subclassesOfA.find(sub => sub.id === subclassId);

  if (subclass) {
    let instance = new subclass();
    instance.someMethod();
  } else {
    console.error('Subclass not found');
  }
}

// Example usage
bar('B', [B, C]); // Output: Method in B
bar('C', [B, C]); // Output: Method in C

Breakdown

  • Each concrete subclass (B, C, etc.) defines a static id property.
  • The abstract base class (A) declares a static identifier property that must be implemented by each subclass.
  • The bar function creates an instance of the matching subclass based on the provided identifier and an array of subclass constructors.

By utilizing static properties and constructors along with an abstract property for identification, the subclasses can be easily distinguished within a collection.

Answer №2

Your code is almost correct, but a few modifications need to be made:

  • To reference `subclass.identifier`, ensure that the property exists in the type of `subclassesOfA`
  • You cannot create an instance of `typeof A`, but you can create an instance of `{ new(): A }`
function foo(subclassIdentifier: string, subclassesOfA: {
  identifier: string,
  new (): A
}[]): void {
    let subclass = subclassesOfA.find((subclass) =>
        subclass.identifier == subclassIdentifier);
    if (subclass) {
        let instance = new subclass();
       // ...
    }
}

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

Dynamically loading classes in TypeScript without using default export

Is there a way to dynamically load classes in TypeScript without using a default export method? I have managed to make it work partly, but I am looking for a solution that doesn't require a default export: export default class Test extends Base { ... ...

The Angular Ivy strictTemplates error message states that the type 'Event' cannot be assigned to the type 'InputEvent' parameter

I'm feeling lost trying to figure out what's wrong with this code snippet: <input #quantity type="number" matInput formControlName="quantity" (input)="onQuantity($event, i)" placeholder="Quantity"/> onQuantity(event: InputEvent, i: number ...

Tips on exporting a basic TypeScript class in an Angular 4 module

I am facing a challenge with packaging a TypeScript class as part of an Angular module for exporting it as a library using ng-packagr. For instance, here is my class definition - export class Params { language: string ; country: string ; var ...

What is the purpose of the 'unique' keyword in TypeScript?

While coding in the TypeScript playground, I stumbled upon a situation where the term unique seems to be reserved. However, I haven't been able to find any official documentation regarding this. https://i.stack.imgur.com/eQq5b.png Does anyone know i ...

What is the best way to retrieve a specific property from an array of objects in Angular 6 using typescript?

I am currently working on a budgeting application that incorporates an array of expenses with a price property. Each expense is defined within an Expense model in Typescript. While I can easily access the price property using ngFor loop in HTML, I'm c ...

Internet Explorer is throwing unexpected routing errors in Angular 2

I have a spring-boot/angular 2 application that is working perfectly fine on Chrome, Safari, Opera, and Edge. However, when accessed through Internet Explorer, the app directly routes to the PageNotFound component. I have tried adding shims for IE in the i ...

Is it possible to enable full screen window functionality in Angular 2 by simply clicking a button? Let's find out

After successfully creating the user login page, I am facing an issue. When the submit button is clicked, the page should navigate to a specific component (test.component.ts and test.component.html). My goal now is to make that window go into full screen m ...

Navigating through object keys in YupTrying to iterate through the keys of an

Looking for the best approach to iterate through dynamically created forms using Yup? In my application, users can add an infinite number of small forms that only ask for a client's name (required), surname, and age. I have used Formik to create them ...

Learning the process of configuring neo4j connection details without relying on environment variables

Is there a way to specify the database connection in code using the Drivine neo4j driver without relying on environment variables? ...

Error: The identifier HTMLVideoElement has not been declared

Encountering an issue while attempting to build my Angular 9 Universal project for SSR: /Users/my-project/dist/server.js:28676 Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__metadata"])("design:type", HTMLVideoElement) ReferenceError: HTMLVideoElem ...

Designing functional components in React with personalized properties utilizing TypeScript and Material-UI

Looking for help on composing MyCustomButton with Button in Material-ui import React from "react"; import { Button, ButtonProps } from "@material-ui/core"; interface MyButtonProps { 'aria-label': string, // Adding aria-label as a required pro ...

What could be the reason for the component not receiving data from the service?

After attempting to send data from one component to another using a service, I followed the guidance provided in this answer. Unfortunately, the data is not being received by the receiver component. I also explored the solution suggested in this question. ...

Adjusting the IntelliSense Functionality in Monaco Editor

Currently, I am testing out the CompletionItemProvider feature for my project. I have implemented two separate CompletionItemProviders. One is set to trigger when any alphabet letter is typed and the other triggers when the user enters a single quote chara ...

Tips for accessing a URL page in Ionic 3 without using the ionic-native plugin

Is there a method to open a specific page when a particular URL is accessed by the browser, without relying on ionic-native for deep linking? This functionality would be beneficial both for the app itself and for development purposes. For instance, can h ...

Using string interpolation within the onclick event (Ionic 2 + Angular 2)

One question that's troubling me - I'm attempting to pass a string within an "onclick" event in my Ionic 2 app, but it keeps throwing an error. <button onclick="window.plugins.socialsharing.shareViaWhatsApp(null, null, '{{sound.file}}&a ...

Encountering a problem with TypeScript while employing Promise.allSettled

My current code snippet: const neuroResponses = await Promise.allSettled(neuroRequests); const ret = neuroResponses.filter(response => response?.value?.data?.result[0]?.generated_text?.length > 0).map(({ value }) => value.data.result[0]?.genera ...

retrieve document data from firestore using the service

Is there a way to get real-time data from a Firestore document using a service? According to Firebase's documentation, you can achieve this by following this link: https://firebase.google.com/docs/firestore/query-data/listen?hl=es#web-modular-api I ...

Tips for invoking a function from one React component to another component

Currently, I am working on two components: one is Game and the other is PickWinner. The Game component serves as the parent component, from which I need to call the pickWinner function in the PickWinner component. Specifically, I want to trigger the startP ...

Update the data and paginator status

In my development project, I have implemented PrimeNG Turbotable with the code <p-table #dt. Based on information from here, using dt.reset() will clear the sort, filter, and paginator settings. However, I am looking for a solution to only reset the pa ...

What is the best method for choosing visible elements within a scrollable container?

Is there a way to retrieve the list of visible elements within a scrollable container? The number of elements that are visible on the screen changes as one scrolls, making it challenging to add a specific class to only the last two visible elements. Any s ...