TypeScript会剥离注释并破坏JSDoc文档。

13

目标是从TypeScript代码获取JSDoc文档。TypeDoc(TypeScript文档解决方案)的文档质量不可接受,因为该文档针对的是JS用户,不应淹没特定于TypeScript实现的细节(接口等)。

当前,将TypeScript编译成ES6并从JS文件生成文档基本可以解决问题。但未分配值的属性除外。

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

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

被转译为

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

虽然我希望得到类似的东西

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

        /**
         * @public
         * @var a
         */
        this.a = true;
    }
}
或者
class A {
    /**
     * @private
     * @var _a
     */

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

在TypeScript中,如何为未分配的类成员提供注释(特别是JSDoc)?是否有任何技巧可以使注释保持原样(即使 private _a; 在转译后的代码中不存在)?


我可以问一下你为什么需要这个功能吗?当然,有一些技巧和解决方法可以保留这些注释,但目前我完全看不出它存在的理由。 - John Weisz
1
@JohnWhite 我发现在 TypeScript 中生成 JSDoc 文档除了从转义后的 ES6 中生成文档之外别无良策,而且这种行为会导致大量属性没有被记录到文档中。 - Estus Flask
4个回答

2
有没有什么诀窍可以让注释保持在原地?
你几乎肯定不想要这个。例如,以下代码不产生输出:
/** an interface */
interface Q { }

如果编译器保留文档,它们在许多情况下(几乎所有情况)都是多余且具有误导性的。如果出现在class C上方,则文档似乎适用于该类(如果该类本身没有文档,则更加混乱)。如果我正在阅读您问题中的示例,我会想知道this.a是否实际上是@private,如第一个文档所述,还是@public。对于其他程序员和工具来说,解释这些杂散的文档并没有正确的方法。
当TS和JS之间存在直接对应关系时,编译器会保留文档,严格来说,因为这样做没有任何危害(即使效用非常小)。但是,编译器不应保存与其所适用代码分离的文档。
查看文档的最佳位置是直接在TypeScript源代码中(在您的编辑器中或通过sourcemaps)。TypeScript文件是您的源代码,而不是生成的JS:使用TypeScript文档生成器。已编译或转译的代码不是文档的有用位置,保留问题所问的特定文档将具有误导性。

这可能是正确的,但我肯定想要这个。问题首先涉及JSDoc注释。它们旨在为不必成为代码读者的文档读者提供。将它们放入转换后的JS中是生成JSDoc文档的方法,而不是阅读它。请注意@var标记的使用,它被特别放置在那里以帮助正确解析错位的注释。 - Estus Flask
你正在使用TS编写JS库,希望能够获得仅限JS的文档 - 这是非常合理的。你希望TS编译器可以以一种有助于你读取转换后的JS文件的方式生成代码。但它并没有这样做,我同意这让人失望,但有时最有帮助的答案就是“你不能”(但我不能只发这个)。与其试图改变编译器,为什么不向TypeDoc项目提交一个省略与typescript相关文档的标志的请求呢?这似乎是一个合理的请求。然后你就不需要冗余的 @public@var 了。 - mk.

1

解决方法:

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

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

[游乐场]

问题:

在TypeScript中,如何为未分配的类成员提供注释(特别是JSDoc)?

如果您查看负责构造函数生成的源代码:
  • emitConstructor [line]
  • |--- emitPropertyDeclarations(node, getInitializedProperties(node, /*isStatic*/ false)); [line]
  • |------ getInitializedProperties [line]
您会发现,无法定制行为。 现有的文档生成工具 极端方法: TypeScript提供编译器API:https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API。可能,您可以遍历源代码并生成任何您喜欢的文档。

是的,谢谢,这确实是解决方案。但它会生成相当多的无用代码(并且可能破坏inhasOwnProperty部分)。 - Estus Flask
你能否在你的问题中添加所需的输出? - MartyIX

1

有什么技巧可以让注释保持在原地吗?

有点儿。您可以明确指定默认构造函数并将其他已分离的注释附加到它上面。更改:

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;
}

这将改变输出结果:

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

to:

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

这正是您想要的。这是最简洁的技巧。

感谢您的建议,虽然这会影响风格,但作为临时解决方案是可以接受的,直到在TypeScript本身中解决此问题。 - Estus Flask
这只是一个部分解决方案。将最后一个代码片段中显示的输出添加所需的 /** @class */MyClass 前面,然后使用 jsdoc 处理。在文档中,您将获得类,但它不会有任何属性。jsdoc 无法使用 IIFE 的 doclets。为了被 jsdoc 使用,它们需要有 @name 标签,以提供完整名称,使它们成为 MyClass 的成员。 - Louis

0

把 _a 定义为未定义的,就像这样:

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

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

这将被编译为

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

编辑:

如果您希望hasOwnProperty的行为与构造函数中的_a完全相同,请将其删除:

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

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

    constructor() {
        delete this._a;
    }
}

这将被编译为

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接