What is the type of a class that extends an abstract class?

Viewed 364

I have an abstract class, and classes that extend it in an array. What do I type the array?

abstract class AbstractClassToExtend {
    constructor() { console.log("hello") }
}

class One extends AbstractClassToExtend {}
class Two extends AbstractClassToExtend {}

const array = [One, Two] // what do i type this array?

I've tried const array: typeof AbstractClassToExtend[] = [One, Two], but when making an instance of one of the classes in the array,

new array[0]()

it gives an error:

error TS2511: Cannot create an instance of an abstract class.

I'm using Typescript 4.3.2.

3 Answers

You specifically want to instantiate the types in the array, so I'm not sure you can type the array as having abstract classes.

Perhaps this is not as elegant as you hoped, but you could have a concrete base implementation from which the others inherit:

abstract class AbstractClassToExtend { }

class BaseImplementation extends AbstractClassToExtend { }

class One extends BaseImplementation { }
class Two extends BaseImplementation { }

const array: (typeof BaseImplementation)[] = [One, Two]

const a: BaseImplementation = new array[0]();
const b: BaseImplementation = new array[1]();

An alternative is to have an array of functions that return an instance of your abstract type:

const functions: Array<() => AbstractClassToExtend> = [() => new One(), () => new Two()];

const c: AbstractClassToExtend = functions[0]();
const d: AbstractClassToExtend = functions[1]();

I got this working in a slightly different way that the accepted answer and it may suit some use cases where creating a BaseImplementation is not ideal.

Instead of using the class constructor directly, add a generic static method to initialize the abstract class. This allows you to have a this type that is initializable.

abstract class AbstractClassToExtend {
  constructor() {
    console.log("hello")
  }

  static init<T extends AbstractClassToExtend >(this: new () => T) {
      return new this()
  }
}

Then add new () => AbstractClassToExtend to the type of the array's items. This makes the array items compatible with calling AbstractClassToExtend.init().

const array: (typeof AbstractClassToExtend & (new () => AbstractClassToExtend))[] = [One, Two]

Use the init method to create an instance instead of new array[0]():

const array: (typeof AbstractClassToExtend & (new () => AbstractClassToExtend))[] = [One, Two]
const one: AbstractClassToExtend = array[0].init()
const two: AbstractClassToExtend = array[1].init()
Related