7

I am trying out some language constructs in Typescript. I am looking to make an array of classes, where the classes are later instantiated. The code I have seems to compile and works if I try it on the Typescript Playground but it does give an error in the typescript box: Argument of type 'typeof Greeter' is not assignable to parameter of type 'BaseGreeter'.

I guess this is because the array definition expects instances of classes that extend BaseGreeter, not the classes themselves that extend it. Is that correct? If that is the case, how do I define an array of classes that extend BaseGreeter?

This is the code:

class BaseGreeter {
    greeting: string;
    greeting_start: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return this.greeting_start + ", " + this.greeting;
    }
}

class Greeter extends BaseGreeter{

    constructor(message: string) {
        super(message);
        this.greeting_start = "Hello";
    }
}

class Greeter2 extends BaseGreeter {
    constructor(message: string) {
        super(message);
        this.greeting_start = "Bye";
    }
}

class Greeter3 extends BaseGreeter {
    constructor(message: string) {
        super(message);
        this.greeting_start = "Adieu";
    }
}


function getGreeters() :[string, BaseGreeter[]]{
    let greeters = new Array<BaseGreeter>();
    greeters.push(Greeter);
    greeters.push(Greeter2);
    greeters.push(Greeter3);
    return ["test", greeters];
}

let foo = {};
let retval = getGreeters();
alert(retval[0]);
foo['greeters'] = retval[1];
var i = 0;

let button = document.createElement('button');
button.textContent = "Say Hello";
button.onclick = function () {
    let greeter = new foo['greeters'][i]("cruel world");
    alert(greeter.greet());
    i = i + 1;
}

document.body.appendChild(button);
4
  • 3
    It would be new Array<typeof BaseGreeter>(...) but using the array constructor is a terrible practice. You don't need to specify a type at all anyway, even if you do use the constructor, because it will be inferred from the elements. So: direct fix islet greeters = new Array<typeof BaseGreeter>(); but that's bad code - write let greeters = [Greeter1, Greeter2, Greeter3]; Commented Apr 24, 2018 at 5:29
  • 1
    Array constructor is not terrible, just totally unnecessary if length argument is not provided. Commented Apr 24, 2018 at 5:39
  • 1
    Well I tend to think of "totally unnecessary" as terrible :p seriously though, I would as soon call Array as a function to specify the length but I can't remember the last time I needed to do that. Commented Apr 24, 2018 at 5:42
  • I agree totally unnecessary is indeed terrible. :) It wans't the real code I was going to write, just testing some things out. But thanks for pointing it out. Commented Apr 24, 2018 at 6:14

1 Answer 1

7

BaseGreeter type means that a value conforms to BaseGreeter interface and is an instance of BaseGreeter class. The type that designates BaseGreeter constructor is typeof BaseGreeter:

function getGreeters(): [string, typeof BaseGreeter[]]{
    let greeters = new Array<typeof BaseGreeter>();
    greeters.push(Greeter);
    greeters.push(Greeter2);
    greeters.push(Greeter3);
    return ["test", greeters];
}
Sign up to request clarification or add additional context in comments.

1 Comment

Note that typeof BaseGreeter may well be too specific for some use cases (other questions marked as duplicates of this one), such as when the BaseGreeter analog is an abstract class. In such cases you might want to use new (message: string) => BaseGreeter instead, meaning "something with a construct signature that takes a string argument and returns something assignable to BaseGreeter".

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.