Search code examples
javascriptdocumentation-generationgoogle-closure-compilerjsdoc

How to document a type returned by a function (not constructor), and its methods


I'm using JsDoc3 and the Closure Compiler. I use a JsDoc template which outputs JSON for me to make HTML from it with a custom script.

I have a function which returns a custom class (but I don't have, nor need, a constructor), and I want to be able to document it (the function), and the functions present in the returned object.

myLib.defer = function() {

    return {
        'then':    function() {},
        'resolve': function() {},
        'reject':  function() {},
        'notify':  function() {},
        'promise': function() {}
    };

};

How do I document this? I don't have a constructor, nor I need one. I do not care that much about documenting the "class" (I don't need one, and besides, the object returned is documented in the @return of this function), the only thing I need is to document the functions that are present in the returned object, so they end up in the JsDoc's JSON output for me to grab.

Also, I'm using Closure Compiler's AVANCED_OPTIMIZATIONS mode, so if any workaround's side effects (such as useless code) will be supressed by that, it's a good workaround.


Solution

  • Unfortunately there is no way to do this that doesn't look awkward, but this works.

    Firstly, one can document a class that doesn't have a constructor with an interface, as Chad mentioned. It looks like this:

    /**
     * Description of the Deferred interface.
     * @interface Deferred
     */
    function Deferred() {}
    

    And one can document methods of it like this (for example):

    /**
     * Returns a Promise
     * @function Deferred.promise
     * @return {Promise} A Promise.
     */
    Deferred.prototype.promise = function() {};
    

    Then, on the function that returns your constructor-less class, you annotate your return like this:

    myLib.defer = function() {
    
        /** @type {Deferred} */
        return {
            'then':    function() {},
            'resolve': function() {},
            'reject':  function() {},
            'notify':  function() {},
            'promise': function() {}
        };
    
    };
    

    However, this will only pass Closure Compiler; it won't get rightly converted by the JsDoc3 JSON export template (maybe the regular one too), so we need this @class annotation (which CC ignores):

    /**
     * Description of the Deferred interface.
     * @interface Deferred
     * @class Deferred
     */
    function Deferred() {}
    

    With all of this in place, our docs will be generated correctly, and our code will compile to the same it did before - all the boilerplate is removed as dead code upon compilation.

    Update: As Chad mentions, you wouldn't get a warning if you tried to instantiate a non-interface directly, which can be bad, and you may not like that (Interfaces will throw an error if instantiated).

    One way to go about this is making the interface constructor @private to a @const object literal, as mentioned in his answer to that question.