Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass an array of constructors

Tags:

typescript

How is it possible to pass an array of functions that return object of some type. In another words - array of constructors?

What I want to do is something like this:

    constructor(name: string, systems: Array<Function>){
        this.system = new systems[0]();
    }

but I got an error Cannot use 'new' with an expression whose type lacks a call or construct signature. and as I understand I should somehow let compiler know what type of objects that constructor returns but I don't know how.

Assuming RJM's answer, here is a bit extended example of what I mean:

interface System{
    name: string;
}
interface Component{
    blahblha: string;
}
class Doh implements Component{
    blahblha: string;
}
class Bar implements System{
    name: string = 'bar';
} 

class Blah implements System{
    name: string = 'foo';
}
class Foo {
    systems: Array<System>;
    constructor(bars: Array<()=>System>) {
        for (let i in bars){
            this.systems.push(new bars[i]()); // error: only a void functions can be called with the 'new' keyword.
        }
    }
}
var foo = new Foo([Blah]); // error: Argument of type 'typeof Blah[]' is not assignable to parameter of type '(() => System[]'. Type 'typeof Blah[]' is not assignable to type '() => System'.

I want to ensure that Foo.systems will be populated only with instances of objects that implements System interface. So I should have error doing something like new Foo([Doh]); but I'm getting error even when passing Blah

like image 394
SET Avatar asked Jan 10 '16 16:01

SET


1 Answers

If you want to instantiate classes you will have to refer to it's constructor using the new keyword.

function factory(constructors: { new (): System }[]) {
    let constructed = constructors.map(c => new c());
}

interface System {
    name: string;
}
class SomeClass implements System {
    name: string;
    constructor() { }
}

class SomeOtherClass implements System {
    name: string;
    constructor() { }
}

let a = factory([SomeClass, SomeClass, SomeClass, SomeOtherClass]);

The factory function will receive a list of constructors that can create something of that satisfies the System interface.

Calling the method with a class that does not implement System will result in the error you are receiving now.

class AnotherClass {}
factory([AnotherClass]); // error

Structural typing works as well, so you don't have to implement the interface explicitly.

class AnotherClass {
    name: string
}
factory([AnotherClass]);

To make the code a little bit cleaner you can move the interface described for the constructors parameter to an interface declaration.

// an interface that describes classes that create instances that satisfy the System interface
interface CreatesSystem {
    new (): System
}

interface System {
    name: string;
}

function factory(constructors: CreatesSystem[]) {
    let constructed = constructors.map(c => new c());
}
like image 154
toskv Avatar answered Oct 04 '22 21:10

toskv