Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript doesn't allow typeof inheritance of classes with different constructor signatures

I am implementing a commandBus, and I want to register command types with command handlers to map incoming commands to the right command handler. I passed a command constructor to map to a handler constructor like so register(handler : typeof Handler, command : typeof Command). But I kept getting compilaton errors.

I finally isolated the reason. In typescript, you can't define an argument arg typeof X and pass in a constructor Y, event if 'Y extends X. The constructors must have the same signature.

Check that snippet. At the bottom, even commandBus.register(Object) does not throw compilation errors.

class BaseClass{}
class DerivedClass extends BaseClass{
    constructor(b : number) {
        super();
    }
}
class AnotherClass extends BaseClass{
    constructor(){
        super();
    }
}
class CommandBus {
    register(handler : typeof BaseClass) {}
}
var commandBus = new CommandBus();
commandBus.register(DerivedClass); // Argument of type 'typeof DerivedClass' is not assignable to parameter of type 'typeof BaseClass'.
commandBus.register(AnotherClass); // Compiles
commandBus.register(Function); // Compiles
commandBus.register(Object); // Compiles

The only way I got this to work is to add an overload constructor signature

class DerivedClass extends BaseClass{
    constructor(b? : any);
    constructor(b : number) {
        super();
    }
}

But is it me or this is damn ugly?

Can anyone point me into a direction on how to get rid of these compiler errors without a dirty hack of adding useless overload signature all over the place?

like image 791
Ludovic C Avatar asked Oct 30 '15 18:10

Ludovic C


1 Answers

What you're really trying to say here is that you want a constructor function for a BaseClass that takes any number of arguments. You can write that type:

class CommandBus {
    register(handler: new(...args: any[]) => BaseClass) {}
}

Note that the error is 100% correct. If you had written

class CommandBus {
    register(handler : typeof BaseClass) {
       var x = new handler();
    }
}
var c = new CommandBus();
c.register(DerivedClass);

You'd be passing zero arguments to the DerivedClass constructor.


These lines down here

commandBus.register(Function); // Compiles
commandBus.register(Object); // Compiles

only compile because BaseClass has no members (remember, TypeScript uses a structural type system, so the empty type is assignable from any type!). If you add any properties or methods to BaseClass that aren't in Function or Object, these lines become errors.

like image 171
Ryan Cavanaugh Avatar answered Sep 28 '22 14:09

Ryan Cavanaugh