12

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)?

Estus Flask
  • 150,909
  • 47
  • 291
  • 441
  • May I ask why you need this functionality? Sure there are tricks and workarounds to keep these comments in place, but currently I see absolutely no reason for this to exist. – John Weisz Feb 05 '16 at 13:26
  • 1
    @JohnWhite I've found no good way to generate JSDoc documentation in TypeScript other than from transpiled ES6. And with this kind of behaviour a bunch of properties remains undocumented. – Estus Flask Feb 05 '16 at 13:57

4 Answers4

2

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.

mk.
  • 10,056
  • 5
  • 35
  • 52
  • This may be correct, but I certainly do want this. The question refers to JSDoc comments in the first place. They are indended for documentation readers who don't have to be code readers. Putting them to transpiled JS is the way to generate JSDoc documentation, not to read it. Notice the usage of @var tag, it was placed there especially to help displaced comments to be properly parsed. – Estus Flask Feb 11 '16 at 11:49
  • You are writing a JS library in TS and you want JS-only docs - perfectly reasonable. You are hoping that the TS compiler might emit code in a way that facilitates your workaround of reading the transpiled JS with JSDoc. It doesn't do that, and I agree that it is disappointing, but sometimes the most helpful answer is just "you can't" (but I couldn't post only that). Instead of trying to change the compiler, why not file a request with the TypeDoc project for a flag that omits docs related to typescript? Seems like a reasonable request. And then you won't need the redundant `@public` or `@var`. – mk. Feb 11 '16 at 16:59
1

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.

Louis
  • 128,628
  • 25
  • 249
  • 295
Martin Vseticka
  • 25,160
  • 25
  • 118
  • 187
1

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.

Roman Pletnev
  • 5,788
  • 2
  • 19
  • 28
  • Thanks for the suggestion, thus hurts the style but is admissible as a temporary solution, until it will be sorted out in TypeScript itself. – Estus Flask Feb 13 '16 at 10:26
  • This is, at best, a partial solution. Take the output shown in the last code snippet and just add the requisite `/** @class */` in front of `MyClass` and then process with jsdoc. In the documentation, you'll get the class but it won't have any properties. jsdoc is unable to use the doclets that are part of the IIFE. In order to be used by jsdoc, they'd need to have the `@name` tag to give a full name that makes them members of `MyClass`. – Louis Nov 30 '16 at 12:54
0

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;
}());
Markus
  • 3,053
  • 3
  • 20
  • 25