Estus Flask
Estus Flask

Reputation: 222319

TypeScript strips down comments and spoils JSDoc documentation

The objective is to get JSDoc documentation from TypeScript code. The quality of documentation from TypeDoc (TypeScript documentation solution) isn't acceptable because the documentation is targeted at JS users and shouldn't be flooded with the details that are specific to TypeScript implementation (interfaces, etc).

Currently transpiling to ES6 and generating the documentation from JS files does the trick for the most part. Except for the properties that have no assigned values. As it appears,

class A {
    /**
     * @private
     * @var _a
     */
    private _a;

    /**
     * @public
     * @var a
     */
    public a = true;
}

is being transpiled to

class A {
    constructor() {
        /**
         * @public
         * @var a
         */
        this.a = true;
    }
}

While I would expect something like

class A {
    constructor() {
        /**
         * @private
         * @var _a
         */

        /**
         * @public
         * @var a
         */
        this.a = true;
    }
}

or

class A {
    /**
     * @private
     * @var _a
     */

    constructor() {
        /**
         * @public
         * @var a
         */
        this.a = true;
    }
}

How can comments (particularly JSDoc) be provided for unassigned class members in TypeScript? Is there a trick that could make the comments stay in place (even if private _a; is absent from transpiled code)?

Upvotes: 13

Views: 3202

Answers (4)

MartyIX
MartyIX

Reputation: 28648

Workaround:

class A {
    /**
     * @private
     * @var _a
     */
    private _b = undefined;

    /**
     * @public
     * @var a
     */
    public a = true;
}

[Playground]

Question:

How can comments (particularly JSDoc) be provided for unassigned class members in TypeScript?

If you have a look at the source code responsible for constructor generation:

  • emitConstructor [line]
  • |--- emitPropertyDeclarations(node, getInitializedProperties(node, /*isStatic*/ false)); [line]
  • |------ getInitializedProperties [line]

You can see, it is not possible to customize the behavior.

Existing tools for documentation generation

Hardcore approach:

TypeScript provides compiler API: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API. Possibly, you can traverse source code and generate whatever documentation you like.

Upvotes: 1

Markus
Markus

Reputation: 4149

Define _a as undefined, like this:

class A {
    /**
     * @private
     * @var _a
     */
    private _a = undefined;

    /**
     * @public
     * @var a
     */
    public a = true;
}

this compiles to

class A {
    constructor() {
        /**
         * @private
         * @var _a
         */
        this._a = undefined;
        /**
         * @public
         * @var a
         */
        this.a = true;
    }
}

Edit:

If you want that hasOwnProperty behave exactly same delete _a in the constructor:

class A {
    /**
     * @private
     * @var _a
     */
    private _a = undefined;

    /**
     * @public
     * @var a
     */
    public a = true;

    constructor() {
        delete this._a;
    }
}

this compiles to

var A = (function () {
    function A() {
        /**
         * @private
         * @var _a
         */
        this._a = undefined;
        /**
         * @public
         * @var a
         */
        this.a = true;
        delete this._a;
    }
    return A;
}());

Upvotes: 0

Roman Pletnev
Roman Pletnev

Reputation: 6138

Is there a trick that could make the comments stay in place?

Somewhat. You can explicitly specify the default constructor and attach the otherwise detached comments to it. Change:

class MyClass {
    /**
     * @private
     * @var _a
     */
    private _a;

    /**
     * @private
     * @var _b
     */ 
    private _b;
}

to:

class MyClass {
    /**
     * @private
     * @var _a
     */

    /**
     * @private
     * @var _b
     */

    constructor() {}

    private _a;

    private _b;
}

This will change the output from:

var MyClass = (function () {
    function MyClass() {
    }
    return MyClass;
})();

to:

var MyClass = (function () {
    /**
     * @private
     * @var _a
     */
    /**
     * @private
     * @var _b
     */
    function MyClass() {
    }
    return MyClass;
})();

Which is exactly what you want. This is the "cleanest trick" to be found.

Upvotes: 1

mk.
mk.

Reputation: 11710

Is there a trick that could make the comments stay in place?

You almost certainly don't actually want this. For example, the following code produces no output:

/** an interface */
interface Q { }

If the compiler were to preserve the docs, they would in many (nearly all) cases be superfluous and misleading. If that appeared above a class C, then the doc would seem to apply to the class (even more confusing if that class had no docs of its own). If I were reading the example in your question, I would wonder whether this.a was actually @private as the first doc claimed, or @public. There is no right way for other programmers and for your tools to interpret these stray docs.

The compiler preserves docs when there is a direct correspondence between the TS and JS, strictly because there is no harm in doing so (even though there is very little utility). But it does not, and should not, preserve docs that are detached from the code they apply to.

The best place to look at docs is directly in the TypeScript source (in your editor, or via sourcemaps). It's the TypeScript file that is your source, not the emitted JS: use a TypeScript doc generator. Compiled or transpiled code is not a useful place for docs, and carrying over the particular docs that the question is asking about would be misleading.

Upvotes: 2

Related Questions