javascripttypescriptnew-operatorinstantiationtypescript-definitions

What is the right typescript definition for a specific javascript instantiation pattern


I'm currently working on Paper.js library typescript definition and I have trouble finding the right way to document some parts of the API.

The case can be reduced to the following: let's say we have an Animal class which has a static property Dog that is used as a custom constructor for the class:

var Animal = function(type) {};
Animal.Dog = function() {
    return new Animal('dog');
};

An Animal instance can be built in 2 ways:

var dog = new Animal('dog');

Or:

var dog = new Animal.Dog();

In both cases, I need the type of the dog variable to be inferred as Animal.


I first tried:

declare class Animal
{
    constructor ( type )
    static Dog (): Animal
}

But TSLint fails with the error: "Only a void function can be called with the 'new' keyword.", because Animal.Dog() function return type is Animal.

And if I set the return type of Animal.Dog() as void:

declare class Animal
{
    constructor ( type )
    static Dog (): void
}

TSLint pass but I get void as the inferred type...


So I tried another way:

declare class Animal
{
    constructor ( type )
}

declare namespace Animal
{
    export class Dog extends Animal
    {
        constructor()
    }
}

With this, TSLint pass but in the case of:

var dog = new Animal.Dog();

The inferred type of dog variable is Animal.Dog and not Animal as I would want to.

That's not a big problem because Animal.Dog type extends Animal but there is no Animal.Dog in the library so I found this workaround misleading for the user.

Does anyone know a better way to handle this case ?

Edit

Elaborating from @stramski solution, I add to the problem, the fact that Animal.Dog can have multiple signatures (e.g. Animal.Dog() and Animal.Dog(color)) and my goal is to document them separately.


Solution

  • What about something like this:

    declare class Animal
    {
        constructor ( type )
        static Dog : (new () => Animal)
    }
    

    Edit

    As there are overloaded constructors the typing is a little different:

    declare class Animal
    {
        constructor ( type )
        static Dog : (new () => Animal) & (new (color) => Animal)
    }