2

Assuming

abstract class A {};
class B extends A {};
class C extends A {};

I want to accept as a parameter any Subclass of A.

function foo(Subclass: A){
   const obj = new Subclass();
}

This wont work. I get the error

This expression is not constructable. Type 'BaseController' has no construct signatures.

Then I tried

function foo(Subclass: typeof A){   
   const obj = new Subclass();
}

Now I get the error

Cannot create an instance of an abstract class.ts(2511)

Because it is assuming that I am passing A and not a subclass of A.

Is it achievable? How do I get this to work?

Victor Ferreira
  • 6,151
  • 13
  • 64
  • 120
  • 2
    Are you looking for [this](http://www.typescriptlang.org/play/#code/IYIwzgLgTsDGEAJYBthjAgggg3ggvgNwIBQKaGAQggKYAeENAdgCYbZ5FmroIDCtBszZZcBQiRIAzAK5N4ASwD2TBFKVKAFGBkhy6AFwImNAO4JNASgQBeAHxZrOEgiQrICJSABWt42YQdPR4wKwl8SXUtSktiAHo4zwBrYABPaQ1NPliEBOS0jK1MHLyaKCglKAAaUQUMUEgYeCA)? I'm not sure why you are writing `new A()` in your example code, since `A` is always the abstract constructor and not anything you've passed in. Perhaps you mean `new Subclass()`? – jcalz Feb 07 '20 at 01:32
  • my bad. It is `new Subclass()` – Victor Ferreira Feb 07 '20 at 01:32
  • If you are passing a known class to `foo`, is there a reason you don't just construct that class? – Leon Feb 07 '20 at 01:40
  • @jcalz it worked. – Victor Ferreira Feb 07 '20 at 01:41
  • @Leon it is not known. In my specific case it could be any `Controller`. and A is a `BaseController`. – Victor Ferreira Feb 07 '20 at 01:42

2 Answers2

6

I think you should change the annotation to the following:

function foo(subclass: new () => A) {
  const obj = new subclass();
}

Here, we are saying that subclass is newable (i.e., it's a constructor function you call with the new operator), takes no arguments, and constructs a value whose type is assignable to A. This should accept any concrete subclass of A as long as it has a no-arg constructor:

foo(B); // okay
foo(C); // okay

It won't accept A itself, because an abstract class constructor is not considered newable:

foo(A); // error, A is abstract

Oh, as a note for people making example TypeScript code for Stack Overflow questions: Note that TypeScript's type system is structural. With the example definitions you gave, instances of A, B, and C are considered the same, empty type. And all objects are assignable to the empty type, so this also works:

foo(Date); // okay also

To prevent that, you should add properties to A, B, and/or C to distinguish them structurally, such as:

abstract class A {
  a = "A"
};
class B extends A {
  b = "B"
};
class C extends A {
  c = "C"
};

Which will result in more expected behavior:

foo(B); // okay
foo(C); // okay
foo(A); // error, A is abstract
foo(Date); // error, 'a' is missing

Okay, hope that helps; good luck!

Playground link to code

jcalz
  • 264,269
  • 27
  • 359
  • 360
1

For this to work you need to refer to the class by it's constructor reference.

abstract class A {};
class B extends A {};
class C extends A {};

function foo(Subclass: new () => A) {   
   const obj = new Subclass();
}

In this case you can pass both B and C to foo since they have constructors. A will not be accepted since there is no constructor on an abstract class.

Leon
  • 12,013
  • 5
  • 36
  • 59