JavaScript[2] 一直处于近年来最常用的脚本语言之一的地位。它以在 Web 平台上编写脚本的便捷性而闻名。随着语言本身的发展,它从最开始蹭 Java 热度的“玩具”语言,变成了一种成熟的语言,还能用来构建大的应用了。
不幸的是,随着深入使用,JavaScript 语言本身的缺陷也被保留出来,包括:
2014 年,微软推出了 Typescript v1.0[3]。这改变了整个 JavaScript 生态系统。
TypeScript[4] 是 JavaScript 语言的超集,它解决了上一节提到的问题以及更多其他问题。这使得它越来越受欢迎。
State of Js survey 2022[5] 展示的 TypeScript 使用率在上升。
图片
TypeScript 虽然解决了很多问题,但也有缺点。
本文我们将研究 TypeScript 的一个非常好的替代方案——JSDoc,它解决了静态类型和可扩展问题,同时还消除了 JavaScript 生态系统中 TypeScript 的缺点。
JSDoc[6] 是基于 JavaScript 语言注释功能建立起的一套文档系统。可以帮助你在编写 JavaScript 代码的同时,通过使用包含 JSDoc 语法的注释获得文档支持。
JSDoc 语法有多种用途,包括为变量声明类型、指定函数参数和返回值的类型、记录和提供函数的使用方式、避免拼写错误等。这些特性与 TypeScript 类似,可以被像 VS Code 这类现代代码编辑器利用,为程序员提供构建、使用或维护代码的支持。
JSDoc 和 TypeScript 都解决了编写和维护纯 JavaScript 代码的问题。然而,他们使用了不同的策略,各有优缺点。
JSDoc 相对于 Typescript 的优点:
虽然 JSDoc 比 TypeScript 有很多优势。但现状是 Typescript 使用率不断攀升,被大家越来越多地采用,这是有原因的。以下是 Typescript 相对于 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 本身并不强制你处理错误,因此这样做一定程度上有助于改善代码协作、便于维护。
更进一步,你还可以使用 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 用于类的描述。为类添加类型时,重要的是进一步添加类型和描述。
我们使用 @params 关键字来提供需要传递到构造函数中的参数的类型和描述。类中的方法的类型化方式与函数相同,这在上一节中已介绍过,就不再赘述。
除了向代码添加基本类型之外,JSDoc 还有很多方法可以帮助提高可读性性。这里有几个:
/** * 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 1.0.0 * @type {number} */const meaningOfLife = 42
/** * 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 指令则用于将用户引导至生成文档上的相关教程链接。
// jsdoc.js/** @module firstDoc *///The rest of the code goes here
使用 JSDoc 的最大优点之一是能够将 JSDoc 文件转换为生成文档网站——甚至是 Typescript,这样他们就可以获得使用 Typescript 的好处。
如上所述,你可以按照以下步骤生成更具可读性的 GUI:
$ npm install -g jsdoc
$ jsdoc path/to/file.js
这是默认 jsdoc 生成的模板的样子,但你可以设置成不同的模板配置[7]。
TypeScript 中的 .d.ts 文件表示声明文件,你可以使用以下步骤从 JSDoc 代码生成这些文件:
$ npm install tsd-jsdoc
对于单个文件。
$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
本文链接:http://www.28at.com/showinfo-26-80832-0.htmlJSDoc:一个可选的 TypeScript 替代品
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
上一篇: 面试官:听说你很懂线程池?