JSDoc:一个可选的 TypeScript 替代品

开发 前端
JSDoc 语法有多种用途,包括为变量声明类型、指定函数参数和返回值的类型、记录和提供函数的使用方式、避免拼写错误等。这些特性与 TypeScript 类似,可以被像 VS Code 这类现代代码编辑器利用,为程序员提供构建、使用或维护代码的支持。

JavaScript[2] 一直处于近年来最常用的脚本语言之一的地位。它以在 Web 平台上编写脚本的便捷性而闻名。随着语言本身的发展,它从最开始蹭 Java 热度的“玩具”语言,变成了一种成熟的语言,还能用来构建大的应用了。

不幸的是,随着深入使用,JavaScript 语言本身的缺陷也被保留出来,包括:

  • 缺乏静态类型检查。JavaScript 是一门动态语言,有较为宽松的限制。比如:定义的函数参数在调用时不提供也行。静态类型语言(例如 Java)就不是这样了,因为它会在编译时报错,但 JavaScript 默认不提供这方面的支持,就导致某些错误会渗透到 Javascript 应用程序的生产环境中
  • 在大项目中很难扩展和维护。JavaScript 没有提供一种强有力的机制来管理大型代码库,这使得随着时间的推移扩展和维护项目变得困难。

TypeScript 出现

2014 年,微软推出了 Typescript v1.0[3]。这改变了整个 JavaScript 生态系统。

TypeScript[4] 是 JavaScript 语言的超集,它解决了上一节提到的问题以及更多其他问题。这使得它越来越受欢迎。

State of Js survey 2022[5] 展示的 TypeScript 使用率在上升。

图片图片

TypeScript 虽然解决了很多问题,但也有缺点。

本文我们将研究 TypeScript 的一个非常好的替代方案——JSDoc,它解决了静态类型和可扩展问题,同时还消除了 JavaScript 生态系统中 TypeScript 的缺点。

JSDoc 是什么?

JSDoc[6] 是基于 JavaScript 语言注释功能建立起的一套文档系统。可以帮助你在编写 JavaScript 代码的同时,通过使用包含 JSDoc 语法的注释获得文档支持。

JSDoc 语法有多种用途,包括为变量声明类型、指定函数参数和返回值的类型、记录和提供函数的使用方式、避免拼写错误等。这些特性与 TypeScript 类似,可以被像 VS Code 这类现代代码编辑器利用,为程序员提供构建、使用或维护代码的支持。

JSDoc vs Typescript

JSDoc 和 TypeScript 都解决了编写和维护纯 JavaScript 代码的问题。然而,他们使用了不同的策略,各有优缺点。

JSDoc 相对于 Typescript 的优点:

  • 灵活并且代码兼容:JSDoc 只是被定义的一种特殊的 JavaScript 注释,这意味着它可以添加到任何 JavaScript 代码库中(无论语言版本如何),并且它不像 TypeScript 那样与编译器绑定
  • 提供代码注释支持:JSDoc 不仅仅可以用于类型检查。它可用于添加文档说明、描述函数如何工作,并且基于此生成文档网站,所有这些都为增强代码的可维护性和理解提供了价值
  • 无需编译步骤:这是从 TypeScript 切换到 JSDoc 的最直接的原因之一。TypeScript 需要通过编译器将代码编译成 Javascript,以便浏览器可以理解。而 JSDoc 不需要任何编译步骤,因为它本质上只是“注释”,这是 Javascript 本身支持的功能。与每次进行更改时使用必要的 Typescript 构建流程相比,这可以简化并提高开发效率

使用 JSDoc 的缺点

虽然 JSDoc 比 TypeScript 有很多优势。但现状是 Typescript 使用率不断攀升,被大家越来越多地采用,这是有原因的。以下是 Typescript 相对于 JSDoc 的一些优点:

  • 更强的静态类型支持:TypeScript 为类型提供了强大的模型,并能在编译时捕获这些错误。但 JSDoc 就不支持,这些错误就直接留在当时的代码中,也没有手段去要求强制执行修正
  • 类型推断支持:TypeScript 可以从值推断类型。这有助于减少显式类型注释并让代码库更加简洁
  • 转译支持:TypeScript 可以通过其 polyfill 功能使用 JavaScript 语言的最新功能(甚至是更加早期的提案功能),有效地将这些最新代码转换成在低版本浏览器中也能运行的版本

如何使用 JSDoc:基础知识

JSDoc 存在很久了,因此所有现代编辑器中都广泛支持它,开箱即用,无需任何安装。

在 .js 文件中添加 JSDoc 至此,就是增加注释,是通过添加带有额外星号(*)的注释来完成的。

// Normal Javascript Comment 1
/* Normal Javascript Comment 2 */ 

/** 
 JSDoc 需要使用 2 个星号
  */

接下来,我们介绍一些基本功能。

添加代码描述

/** JSDoc 用来服务的语言 */
const language = "JavaScript"

为变量添加类型

/**
 * 本篇文章的作者
 * @type {string}
 */
const writerName = "Elijah"

以上注解表示变量 writerName 是字符串类型。

为对象和数组添加类型

/**
 * @type {Array<string>}
 */
const colours = ['red', 'blue', 'green']

/**
 * @type {Array<number[]>}
 */
const primeNumbers = [1, 2, 3, 5, 7]

以上 2 种方法都是有效的 JSDoc 注解(与 TypeScript 一样)。

而对象类型则可以通过 @typedef 指令来创建。

/**
 * @typeof {Object} User - A user schema
 * @property {number} id
 * @property {string} username
 * @property {string} email
 * @property {Array<number>} postLikes
 * @property {string[]} friends
 */
/** @type {User} */
const person1 = {
  id: 847,
  username: "Elijah",
  email: "elijah@user.com",
  postLikes: [44, 22, 24, 39],
  friends: ['fede', 'Elijah']
}
/** @type {User} */
const person2 = {
  id: 424,
  username: "Winston",
  email: "winston@user.com",
  postLike: [18, 53, 98],
  friends: ['Favour', 'Jane']
}

为函数添加类型(参数、返回值和预期错误类型)

/**
 * Divide two numbers.
 * @param {number} dividend - The number to be divided.
 * @param {number} divisor - The number to divide by.
 * @returns {number} The result of the division.
 */
function divideNumbers(dividend, divisor) {
    return dividend/divisor;
}

@param关键字后面跟参数类型定义,还可以使用连字符 - 添加参数描述。

@returns 关键字用于定义函数返回类型。这对于大型函数特别有用,因为这类函数一般很难观察它预期的返回类型。

此外,你可以使用 @throws 指令添加函数可能的抛错类型。

接下来,改进 divideNumbers 函数,增加除数为零时的抛错支持。

/**
 * Divide two numbers.
 * @param {number} dividend - The number to be divided.
 * @param {number} divisor - The number to divide by.
 * @returns {number} The result of the division.
 * @throws {ZeroDivisionError} Argument divisor must be non-zero
 */
function divideNumbers(dividend, divisor) {
    if (divisor === 0) {
        throw new DivisionByZeroError('Cannot Divide by zero')
    }
    return dividend/divisor;
}

你可以在 @throws 中同时指定错误类型以及错误描述。

/**
 * Custom error for division by zero.
 */
class DivisionByZeroError extends Error {
    constructor(message = "Cannot Divide By Zero") {
      super(message);
      this.name = "DivisionByZeroError";
    }
}

由于 JavaScript 本身并不强制你处理错误,因此这样做一定程度上有助于改善代码协作、便于维护。

为 class 添加类型(描述、构造函数以及方法)

更进一步,你还可以使用 JSDoc 为 class 提供类型支持。

/**
 * A Rectangle Class
 * @class
 * @classdec A four-sided polygon with opposite sides of equal length and four right angles
 */
class Rectangle {
  /**
   * Initializing a Rectangle object.
   * @param {number} length - The length of the rectangle.
   * @param {number} width - The width of the rectangle.
   */
  constructor(length, width) {
    this.length = length;
    this.width = width;
  }

  /**
   * Calculate the area of the Rectangle
   * @returns {number} The area of the rectangle.
   */
  calculateArea() {
    return this.length * this.width;
  }

  /**
   * Calculate the perimeter of the rectangle.
   * @returns {number} The perimeter of the rectangle.
   */
  calculatePerimeter() {
    return 2 * (this.length + this.width);
  }
}

上面是一个简单的矩形类,提供了  2 种方法分别用来计算其面积和周长。

@class 关键字用于表示这个函数需要使用 new 关键字调用,@classdec 用于类的描述。为类添加类型时,重要的是进一步添加类型和描述。

  1. 构造函数
  2. 所有属性和方法

我们使用 @params 关键字来提供需要传递到构造函数中的参数的类型和描述。类中的方法的类型化方式与函数相同,这在上一节中已介绍过,就不再赘述。

改进通用代码文档

除了向代码添加基本类型之外,JSDoc 还有很多方法可以帮助提高可读性性。这里有几个:

  • 添加代码作者:可以使用 @author 指令添加作者姓名和电子邮件
/**
 * Possible title for this article
 * @type {string} 
 * @author Elijah [elijah@example.com]
 */
const articleTitle =  "Demystifying JSDoc"
  • 用法示例:你还可以添加代码片段,展示如何使用,这对于复杂的代码块特别有用
/** 
 * Sums of the square of two numbers a**2 + b**2
 * @example <caption>How to use the sumSquares function</caption>
 * // returns 13 
 * sumSquares(2, 3)
 * @example
 * // returns 41
 * sumSquares(4, 5)
 * // Typing the function
 * @param {number} a - The first number
 * @param {number} b - The second number
 * @returns {Number} Returns the sum of the squares
 */
const sumSquares = function(a, b){
    return a**2 + b**2
}

我们使用 @example 指令来实现这一点,也可以使用 <caption> 标签作为标题。

  • 版本控制:你还可以使用 @version 指令指定项目的版本
/** 
 * @version 1.0.0
 * @type {number} 
 */
const meaningOfLife = 42
  • 有用的链接:通常,你可能希望向用户提供一些跳转链接,他们可以获得有关代码的更多知识。它可能是 GitHub 仓库、一篇教程、一篇博客等。为此,需要两个指令来帮助实现:@link 和 @tutorial
/**
 * How to use the link tags
 * Also see the {@link https://jsdoc.app/tags-inline-link.html official docs} for more information
 * @tutorial getting-started
 */
function myFunction (){
}

@link 指令将“official docs”渲染成指向某个地址的文字链接。而 @tutorial 指令则用于将用户引导至生成文档上的相关教程链接。

  • 创建模块:可以使用文件顶部的 @module 指令在 JSDoc 中创建模块,当前文件就成一个模块了。模块被分组在生成的文档网站上的单独部分中。
// jsdoc.js
/** @module firstDoc */
//The rest of the code goes here

转换 JSDoc 文件

使用 JSDoc 的最大优点之一是能够将 JSDoc 文件转换为生成文档网站——甚至是 Typescript,这样他们就可以获得使用 Typescript 的好处。

从 JSDoc 文件生成文档网站

如上所述,你可以按照以下步骤生成更具可读性的 GUI:

  • 安装 jsdoc
$ npm install -g jsdoc
  • 对目标文件运行 jsdoc
$ jsdoc path/to/file.js
  • 打开生成的网站。jsdoc CLI 会将文档自定输出到 out 文件夹,然后在浏览器中打开 out/index.html

这是默认 jsdoc 生成的模板的样子,但你可以设置成不同的模板配置[7]。

从 JSDoc 生成 .d.ts 文件

TypeScript 中的 .d.ts 文件表示声明文件,你可以使用以下步骤从 JSDoc 代码生成这些文件:

  • 在项目文件夹中安装 tsd-jsdoc
$ npm install tsd-jsdoc
  • 生成 .d.ts 文件

对于单个文件。

$jsdoc -t node_modules/tsd-jsdoc/dist -r our/jsdoc/file/path.js

对于多个文件。

$jsdoc -t node_modules/tsd-jsdoc/dist -r file1.js file2.js file3.js ...

对于整个文件夹。

$jsdoc -t node_modules/tsd-jsdoc/dist -r src

它会将文件中的所有类型合并到 单个文件 out/types.d.ts 中。

注意:这假设你已经安装了上一节中的 jsdoc 。如果没有,请在运行此步骤之前先安装它。

结论

至此,我们已经学习了使用 JSDoc 以及从 JSDoc 代码生成类型和文档网站的基础知识。当 Typescript 编译/构建步骤对生产力产生负面影响时,JSDoc 特别有用。对遗留代码库来说 JSDoc 也很有用。

Rich Harris(Svelte 和 SvelteKit 的创建者)也将整个 Svelte 和 SvelteKit 仓库从 TypeScript 改用 JSDoc[8]。另外,TypeScript 也添加了对许多 JSDoc 声明的支持(来源[9])。

参考资料

[1]JSDoc: A Solid Alternative To TypeScript: https://blog.openreplay.com/jsdoc--a-solid-alternative-to-typescript

[2]JavaScript: https://en.wikipedia.org/wiki/JavaScript

[3]Typescript v1.0: https://devblogs.microsoft.com/typescript/announcing-typescript-1-0/

[4]TypeScript: https://www.typescriptlang.org/

[5]State of Js survey 2022: https://2022.stateofjs.com/en-US/usage/

[6]JSDoc: https://jsdoc.app/

[7]模板配置: https://jsdoc.app/about-configuring-default-template.html

[8]从 TypeScript 改用 JSDoc: https://github.com/sveltejs/kit/discussions/4429

[9]来源: https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html

责任编辑:武晓燕 来源: 写代码的宝哥
相关推荐

2022-06-29 15:40:28

MinecraftMinetest开源

2021-09-04 15:21:39

ZulipSlack开源

2021-11-10 18:40:24

exa命令 ls命令Linux

2021-12-29 18:18:59

开源MedusaShopify

2021-01-05 08:35:24

GNU nanoVim编辑器

2020-11-25 13:48:04

LazPaintPaint.NET开源

2020-12-01 17:46:24

FossilGit

2023-03-29 13:13:34

2022-12-03 15:53:46

开源Linux

2023-02-06 06:21:53

BookStack开源

2021-10-19 09:00:00

KubeMQKubernetes工具

2011-04-12 09:13:51

OpenIndianaSolaris替代品

2020-07-07 09:10:29

VS CodeLinux开源

2022-12-26 07:40:00

Heroku替代品dynos

2010-02-25 09:14:07

2022-04-13 09:26:47

PeergosGoogle开源

2022-03-24 10:54:33

Piwigo开源

2022-01-05 11:21:19

开源工具操作系统

2013-01-28 09:25:54

2022-08-02 10:45:29

AppFlowyNotion开源
点赞
收藏

51CTO技术栈公众号