有JSDoc还需要TypeScript吗

136 阅读4分钟

这听起来是不是很耳熟:你想写一个小型脚本,不管是为页面、命令行工具,还是其他什么类型。你从JavaScript开始,直到你想起写代码时没有类型是多么痛苦。所以你把文件从.js重命名为.ts。然后意识到你已经打开了一个麻烦的玩意儿。

如果你在为一个网站或一个库写代码,你就需要引入编译的步骤。如果你在编写CLI脚本,你可以求助于Deno(它支持TypeScript,开箱即用),但是你需要设置你的IDE来理解Deno的API,而且混合和匹配Deno和node并不总是那么容易。

一旦你在本地完成了所有工作,你就需要考虑如何分发你的代码。你会检查你编译的.js文件吗?你会创建一个CI管道来自动编译你的.ts文件吗?如果你在写一个库,你如何发布你的库,以便它可以被其他项目使用?

你实际上不需要TypeScript

问题是......你不需要为了获得静态类型分析而编写TypeScript!

你可以通过使用JSDoc在JavaScript中获得TypeScript的所有好处

TypeScript所提供的是一个静态类型系统。这意味着类型信息在运行代码中没有影响。当你的TypeScript被执行时,所有的类型信息都会完全丢失(这就是为什么你不写一个类型守卫,就不能测试一个变量是否是某个类型的原因)。

这也意味着TypeScript只是提供给TypeScript分析器的额外类型信息,对运行你代码的JavaScript引擎没有任何意义。当你把TypeScript编译成JavaScript时,它基本上只是从你的代码中删除了所有的类型信息,所以它又变成了有效的JavaScript代码。

JSDoc

在JavaScript诞生25年多后,JSDoc作为一种注释JavaScript代码的方式被引入。它是一种正式的标记语言,允许IDE在开发者看到一个函数时提供额外的上下文。

类似的注释标记存在于大多数语言中,我相信你已经知道它了。这就是它的样子:

/**
 * This is the JSDOC block. IDEs will show this text when you hover the
  * printName function.
   *
    * @param {string} name
     */
     function printName(name) {
       console.log(name)
       }

TypeScript 和 JSDoc

较少人知道的是,JSDoc是你充分使用TypeScript所需要的。TypeScript分析器能够理解用JSDoc写的类型,并给你提供与.ts文件相同的静态分析。

我不会在这里提供完整的语法文档。最重要的是你要知道,几乎所有你能在.ts文件中做的事情,你都能用JSDoc来做。但这里有几个例子:

带有原生类型的函数参数:

/**
        * @param {string} a
         * @param {number} b
          */
          function foo(a, b) {}

使用TypeScript提供的开箱即用的类型:

/**
           * @param {HTMLElement} element
            * @param {Window} window
             */
             function foo(element, window) {}

         /** @type {number[]} */
         let years</code></pre><p>定义对象字面量和函数:</p><pre><code class="jsx">/** @type {{ name: string; age: number }} */
         let person
         
         /** @type {(s: string, b: boolean) =&gt; void} */
         let myCallback</code></pre><p><code>*.d.ts</code>文件中导入类型:</p><pre><code>/** @param {import('./types').User} user */
         const deleteUser = (user) =&gt; {}
         </code></pre><p>定义一个类型供以后使用:</p><pre><code class="jsx">/**
          * @typedef {object} Color
           * @property {number} chroma
            * @property {number} hue
             */
             
             /** @type {Color[]} */
             const colors = [
               { chroma: 0.2, hue: 262 },
                 { chroma: 0.2, hue: 28.3 },
                 ]</code></pre><p>参见官方TypeScript JSDoc<a href="https://link.segmentfault.com/?enc=YIvseDV%2F7OzFtyruVl2t3w%3D%3D.ZrZ87RwNDyoHGTllVHEVQD2JVm1Awn2hMbfAGjuj5aZTArwztnK9ZO%2BzY3PBj7KpYjXFjTwMCQXQshfPgZCAX8rzTQ5h8dex5LaR7nyVLVw%3D" rel="nofollow" target="_blank">文档</a>以获得详尽的列表。</p><blockquote>如果你有复杂的类型,你仍然可以编写你的<code>*.d.ts</code>文件并在你的JSDoc注释中导入它们。</blockquote><p>注意,你仍然需要为typescript设置你的项目(和IDE),你需要创建一个<code>tsconfig.json</code>文件,将编译器选项<code>allowJs</code><code>checkJs</code>设置为<code>true</code></p><pre><code class="json">// tsconfig.json
                 {
                   "compilerOptions": {
                       "allowJs": true,
                           "checkJs": true
                               // ...
                                 }
                                 }</code></pre><h2 id="item-0-4">什么时候写TypeScript</h2><p>虽然完全使用JSDoc进行类型声明是可能的,但这并不是最方便的。TypeScript的语法要好得多,而且不那么重复。</p><p>TypeScript团队创建了一个"作为注释的类型"<a href="https://link.segmentfault.com/?enc=6C8nj5mlKGFaTyEKVPWnXw%3D%3D.a9OmylkU%2BdO6UNZW6lG1ahtnLsMBxNZPxum9vqoKIN6qW3QT7Jxz9bviKoKJIUGB0uGuua%2F8qdK7YZ2%2Bnbzzgg%3D%3D" rel="nofollow" target="_blank">ECMAScript提案</a>,允许你编写TypeScript并在不修改的情况下在JavaScript引擎中运行(JavaScript引擎将把这些类型注释视为注释。)</p><p>但是在这个提案被接受之前,我们只能决定使用JSDoc或者TypeScript工具链。</p><h2 id="item-0-5">建议</h2><p>所以现在我的建议是这样的:</p><ul><li>当你正在做一个有编译步骤的项目时,使用TypeScript没有什么坏处</li><li>但是如果你不需要编译步骤,那么坚持使用JSDoc类型注释可能更容易。</li></ul><p>以上就是本文的全部内容,如果对你有所帮助,欢迎点赞、收藏、转发\~</p>