2

I need to create a function which would expect any type of class which extends another specific class (type, not an instance)

Class A{}

foo(b: typeof 'extends A')

Class B extends A{}

Class C {}


foo(B) => working

foo(C) => not working

How can it be done?

tomermes
  • 22,950
  • 16
  • 43
  • 67

1 Answers1

5

I think you might be confused about what types are. In foo(B), the B is not a type; it is a constructor value. This confusion is pretty common, probably because sometimes types and values can have the same name, such as when you declare a class.

Anyway, if your requirement is that foo() should accept anything which is a constructor of A instances, the following should work:

class A { a: string = "A" }

declare function foo(ctor: new (...args: any[]) => A): void;

class B extends A { b: string = "B" }

class C { c: string = "C" }

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

The type new (...args: any[]) => A means "any constructor which produces instances of type A". Thus foo(B) works because B is a constructor that produces instances of type B, a subtype of A. Thus B does produce A instances, and foo(B) works. But since the type C is not a subtype of A, foo(C) yields an error.


I don't know what you want to do inside the implementation of foo(). If you want to actually use the passed-in constructor, then you probably should think about what the constructor arguments need to be, and limit foo() to only accept constructors supporting those arguments. For example, if you expect foo() to call its constructor with no arguments, you probably should limit it:

class A { a: string = "A" }

class B extends A { b: string = "B" }

class C { c: string = "C" }

// D's constructor requires a string argument
class D extends A { constructor(private d: string) { super(); } }

// ctor must be a no-arg constructor
declare function foo(ctor: new () => A): void;

foo(A); // still okay
foo(B); // still okay
foo(C); // still error
foo(D); // error

Also note that having empty classes like class E {} is not recommended, even for example code, because the TypeScript type system based on structures and not names. Since class A {} and class C {} have the same (empty) structure, if I had kept those types the call foo(C) would not give an error. When in doubt, add differing properties to types you intend to be treated differently by the compiler.

Okay, hope that helps; good luck!

jcalz
  • 264,269
  • 27
  • 359
  • 360