kristofferostlund kristofferostlund - 25 days ago 6
Node.js Question

Typings: Type declaration for JS module exporting instance of class, with prototype of the class

I'm sorry if the title is confusing, I'm not entirely sure how to phrase it better.

I'm trying to wrap my head around writing type declarations by attempting to produce one for this file (which is the source for this npm module), wherein I basically export an instance of the

Eventer
class allowing the module to as a singleton but also contains a property referencing the class to create sandboxed instances. I'm using Visual Studio Code to check if the properties are correct. The module looks something like this:

// tiny-eventer.js

function Eventer () {
this.on = function (eventName, listener) { ... };
this.trigger = function (eventName, args) { ... };
}

Eventer.prototype.Eventer = Eventer;

module.exports = new Eventer();


In another module I can then use the singleton-like
eventer
instance by requiring it, but also create a sandboxed instance calling
new eventer.Eventer()
.

// index.js

var eventer = require('tiny-eventer');

var sandboxedEventer = new eventer.Eventer();

sandboxedEventer.on('an-event', function (args) { console.log('sandboxed ' + args); });

eventer.on('an-event', function (args) { console.log('global ' + args); });

eventer.trigger('an-event', 'is called');
// Prints to console: "global is called"

sandboxedEventer.trigger('an-event', 'is called');
// Prints to console: "sandboxed is called"


This basically means this horrific piece of code is valid too:

// sorry.js

var eventer = require('tiny-eventer');
var otherEventer = new (new (new (new eventer.Eventer()).Eventer()).Eventer()).Eventer();


As for the typings I've tried the following:

declare module 'tiny-eventer' {
...

interface TinyEventItem { listener: (args) => void }

class Eventer {
// Doesn't seem to work as it should
Eventer: Eventer;

on(eventName: String, listener: (args) => void): TinyEventItem;

trigger(eventName: string, args: any): void;
}

export = new Eventer;
}


The above will work with the singleton properts such as
eventer.on
and
eventer.trigger
, but also seems to allow
eventer.Eventer.on
, whereby
new eventer.Eventer()
isn't allowed. It feels like I'm doing this wrong.

How would I got about writing a typings file which declares a class with a property referencing itself?

Answer

I figured out something that produces the result I'm looking for (in vscode's Intellisense at least), although I'm not sure it's a proper solution.

What solved it for me was the following Eventer: new Eventer;

If anyone has a better solution, please post them or update this.

declare module 'tiny-eventer' {
    ...

    interface TinyEventItem { listener: (args) => void }

    class Eventer {
        on(eventName: String, listener: (args) => void): TinyEventItem;

        trigger(eventName: string, args: any): void;

        // This right here seems to do it
        Eventer: new Eventer;
    }

    export = new Eventer;
}