4

In TypeScript I use Java like type Class:

interface Class<T = void> {

    new(...args: any[]): T;

}

However, such type doesn't work with abstract classes:

abstract class Bar {}

class Foo {
   
  test(clazz: Class) { }

  doIt() {
    this.test(Bar);//ERROR
  }
}

The error I get is Argument of type 'typeof Bar' is not assignable to parameter of type 'Class<void>'. When class Bar is not abstract then everything is ok.

I understand, that the problem is that declaring Class we say it is possible to use new operator, but we can't use this operator for abstract classes. However, I need a type, that could be suitable both for non abstract and abstract classes (abstract classes are classes too). If it is possible, could anyone say how to do it?

2
  • this.test(Bar); when you do this you don't pass an instance but the actual definition of the class. But the argument you specified on the function test(clazz: Class) { } does expect and instance of Class. new Bar() would solve this, but that obv won't work with abstract classes. The whole point of abstract classes is that they can't be instances, they have to be implemented. So I'm kinda confused on why you are intending to use this abstract class for this. Commented May 17, 2022 at 18:09
  • @Tea_Lover_418 Sometimes we need to pass not an instance but the class itself. For example, when we want in method to create instances using this class. Commented May 17, 2022 at 18:27

1 Answer 1

1

You can use an abstract construct signature:

type Class<T = void> =
  abstract new (...args: any[]) => T;

You can assign both a regular/concrete contructor and an abstract constructor to such a signature, as desired:

abstract class Bar { }
class Baz { }

class Foo {

  test(clazz: Class) { }

  doIt() {
    this.test(Bar); // okay
    this.test(Baz); // okay
  }
}

Note that, for whatever reason, abstract construct signatures must be in the form of an arrow construct expression (like a function expression); you can't put abstract before new in an object-like construct signature:

interface Oops<T = void> {
  abstract new(...args: any[]): T; // error!
  //~~~~~~ <-- 'abstract' modifier cannot appear on a type member
}

Playground link to code

Sign up to request clarification or add additional context in comments.

6 Comments

But if we then do const instance = new clazz(); we get Cannot create an instance of an abstract class. Is it possible to fix?
I don't see new clazz() in your example code as a use case; is this a followup question? If you need to be able to write new clazz() then clazz must be known not to be abstract, like your original Class definition. If you get something which might be abstract then you can't construct it. It's not clear to me how you want to fix it because it sounds like you want conflicting things.
I understand you. I need a definition for both abstract and non abstract classes. For example, when we pass Class in Java we can later create instances using it. If class is abstract then we get runtime exception, so, we can do the following to check Modifier.isAbstract( clazz.getModifiers() );.
TypeScript isn't Java; the static type system in TypeScript is completely erased from runtime, whereas there is runtime type information in Java (Well, Java generics are erased). Whether or not a class is abstract only matters to the TypeScript compiler; no runtime reflection will tell you this. At runtime, all classes are constructable. Does that make sense?
Ok. But I am still trying to create common class. For example, if we create AbstractClass and ConcreteClass, can we somehow mix them, something like this: export type Class<T = void> = ConcreteClass & AbstractClass => T;
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.