2

I would like to create an interface which defined a set of key value pares where the value is restricted to a typeof classWhichExtendsSpecificSuperClass.

For instance, this interface:

interface MyInterface {
     key: typeof SuperClass
}

and this base class:

class BaseClass extends SuperClass {...}

Applied in this way:

function myFunction(arg: MyInterface) {...};

myFunction({
   key: BaseClass
});

The above code would tell me that typeof BaseClass is not assignable to typeof SuperClass.

Now I get that what I am doing is not correct, but is it possible any other way? I would like to ensure that the only allowable values for "key" are typeof classes that extend SuperClass?

If I was dealing in functions I could use generics, but as far as I know there is no way to express a generic at the object property.

Any help would be great!

Jeremythuff
  • 1,518
  • 2
  • 13
  • 35

1 Answers1

1

If all the classes that extend SuperClass have compatible constructor parameters, your code should work with no error. For example:

class SuperClass {
  superValue: string;
}
interface MyInterface {
     key: typeof SuperClass
}
class BaseClass extends SuperClass {
  baseValue: string;
}
function myFunction(arg: MyInterface) { };
myFunction({
   key: BaseClass // no error
});

There's no problem here because both BaseClass and SuperClass can be constructed with zero arguments. But if you change the constructor of BaseClass:

class BaseClass extends SuperClass {
  baseValue: string;
  constructor(someArgument: string) {
    super();
  }
}

you will get the error you mentioned when you call myFunction():

Argument of type '{ key: typeof BaseClass; }' is not assignable to parameter of type 'MyInterface'.
  Types of property 'key' are incompatible.
    Type 'typeof BaseClass' is not assignable to type 'typeof SuperClass'.

Now, depending on what you want to do with MyInterface, that error might be important. Do you intend to take the key property of MyInterface value and use it to construct an instance of some subclass of SuperClass? For example:

declare let myInterface: MyInterface;
let someInstance = new myInterface.key();

If so, then you need to make sure that the class constructor accepts the right number and type of arguments, and that error is helping you. It's telling you to change the BaseClass constructor to be compatible with SuperClass's constructor. For example, if we make someArgument optional:

class BaseClass extends SuperClass {
  baseValue: string;
  constructor(someArgument?: string) {
    super();
  }
}

then the error goes away because new SubClass() will now work with no arguments.


If you don't intend to use MyInterface to construct instances, and you don't care about the number and type of arguments that the constructors take, and you just want it to hold some class constructor for some reason... then you can do this:

type Constructor<T> = {
  new(...args: any[]): T; // any number and type of arguments
}
interface MyInterface {
     key: Constructor<SuperClass>
}

Now myFunction() will work even if the constructor signatures are incompatible. That's because you don't really want to restrict yourself to typeof SuperClass; you'd like to accept any constructor that creates a SuperClass, and that's what Constructor<SuperClass> means. So the error goes away and you're happy.


Hopefully one of those two solutions will work for you. Good luck!

jcalz
  • 264,269
  • 27
  • 359
  • 360