typescripttypesabstract-classabstracttypescript-types

Instantiate class from a collection of subclasses, while maintaining access to static members in Typescript


I think I may have stumbled into a situation where what I want is best described by having both static and abstract on a member of an abstract class, but that doesn't seem to be possible in typescript.

The closest I can get is the 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 tried a variety of different approaches to the collection type. The closest I could get working was a Map from the static member type to instances of the class, but then you have to individually map the values to their classes.

Another thing I tried was to use a Newable<T> type that describes its constructor, but this removes the ability to refer to the subclass' static properties (as does seemingly anything else that makes the constructor available).

Is there a way to both refer to a subclass' static properties and its constructor when having retrieved it from a collection of classes?


Solution

  • Your code works almost as-is, with the following changes:

    function foo(subclassIdentifier: string, subclassesOfA: {
      identifier: string,
      new (): A
    }[]): void {
        let subclass = subclassesOfA.find((subclass) =>
            subclass.identifier == subclassIdentifier);
        if (subclass) {
            let instance = new subclass();
            // ...
        }
    }