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

Just starting out with React and encountering the error: Invalid element type, a string was expected

I seem to be going in circles with the following issue as I try to load the basics of a React app into the browser. An error message stating 'Element type is invalid: expected a string (for built-in components) or a class/function (for composite c ...

The concept of passing arguments in Typescript

During my experience with Typescript programming, I encountered a situation like the one described below. If I pass an argument containing an object with the same name as the parameter defined in the function signature, Typescript recognizes it, but not ...

Produce new lines of code using the vscode.window.activeTextEditor.edit method in Visual Studio Code

Hey everyone, I'm currently working on a vscode extension that can automatically generate template code based on the language you are using when you click a button. However, there seems to be an issue with the formatting of the generated code as it do ...

Property of object (TS) cannot be accessed

My question relates to a piece of TypeScript code Here is the code snippet: export function load_form_actions() { $('#step_2_form').on('ajax:before', function(data) { $('#step_2_submit_btn').hide(); $(&ap ...

The input of type 'Observable<true | Promise<boolean>>' cannot be assigned to the output of type 'boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree>'

I'm currently using a Guard with a canActivate method: canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { return this.fi ...

What is the best way to transform this date string into a valid Firestore timestamp?

Currently, I am facing an issue in my Angular application that is integrated with Firebase Firestore database. The problem lies in updating a date field from a Firestore timestamp field. To provide some context, I have set up an update form which triggers ...

"Resulting in 'undefined' due to an asynchronous function call

Encountering an issue with async method implementation. In my authServices, there is a loginWithCredential function which is asynchronous: async loginWithCredential(username, password){ var data = {username: username, password: password}; api.pos ...

Is there a way to loop through objects in Angular 2

I am working with an Array of Objects named comments, and my goal is to select only the ones that have a specific post id and copy them to another array of objects. The issue I am facing is finding a way to duplicate the object once it has been identified. ...

Using create-react-app with TypeScript for server-side rendering

My current project is built with create-react-app using typescript (tsx files). I'm now interested in implementing SSR for the project, but I'm not exactly sure where to begin. In the past, I've successfully implemented SSR with typescript ...

Jest tests reveal potential errors indicating an object may be null

When running my Jest (typescript) test cases on mongoose Models, I encounter numerous errors such as: Error TS2531: Object is possibly 'null'. For example, consider the following code snippet where the error is reported on line 3: const user = ...

Compile time extraction of constant string from type field

I am currently facing an issue with a field in my type that contains a constant string literal. My goal is to be able to reference both the type and field by name so that I can utilize this string literal throughout my code. Here is an example: export type ...

Issue with MongoDB $push within an Array of Arrays: The shorthand property 'elements' does not have a value available in scope

I am encountering an issue while trying to insert data into Elements in MongoDB using TypeScript. Any suggestions on how to resolve this problem? Attempt 1 Error: I am receiving an error message stating "No value exists in scope for the shorthand property ...

Transmitting image data (blob) from the frontend to the backend using Next.js and tRPC (T3 stack)

I'm currently facing a challenge in sending a leaflet map image from the backend to the frontend using the leaflet-simple-map-screenshoter library for capturing the image. The library returns a blob, which I need to transmit back to the backend and sa ...

I am unable to utilize Local Storage within NextJS

type merchandiseProps = { merchandises: merchandiseType[]; cart?:string, collection?:string, fallbackData?: any }; const MerchandiseList: FC<merchandiseProps> = ({ merchandises }) => { const [cart, setCart] = useState<merchandiseType ...

Is there a way to enable code completion for Firebase on VS Code?

After successfully setting up Typescript for code completion following the guidelines provided in this resource, I now want to enable code completion for Firebase in VS Code. However, I am unsure of the steps to achieve this. How can I activate code compl ...

Error in Typescript: Draggable function is undefined

I'm currently working with typescript alongside vue and jquery ui. Encountering the error "TypeError: item.$element.draggable is not a function". What am I doing wrong in my code? I have already included jquery-ui, as shown in the following files. M ...

Issues arising from an aging Angular project

I'm currently facing an issue with installing node and typescript for an older angular project that I have inherited. This project is using angular 2.4.10 After downloading and installing node version 6.9.5 from the executable file, I proceeded to in ...

I have successfully set up micro-cors on my system, and as I tried to compile, I received the following outcome

While working on the Next.js Stripe project, I ran into an unexpected problem that I need help with. ./src/pages/api/webhooks.ts:3:18 Type error: Could not find a declaration file for module 'micro-cors'. 'E:/Project/longlifecoin/node_module ...

Encountering Thumbnail Type Error While Implementing Swiper in Next.js

When trying to integrate Swiper with Next.js, I ran into an issue concerning thumbnails. "onSwiper={setThumbsSwiper}" This line is causing a TypeScript error: swiper-react.d.ts(23, 3): The expected type comes from property 'onSwiper' w ...

Merge two input fields into one to send data to the backend

I have created two input fields, one for selecting a date and the other for entering a time. Before submitting the form to the backend, I need to combine these two inputs into one variable. For example, <input type="text" name="myDate"> and <input ...