Loading... > 本文首发[「码上花甲」公众号](https://mp.weixin.qq.com/s?__biz=MzIzMDM4MzE3Mw==&mid=2247491579&idx=1&sn=2e62f194b9bf7493a908b8859a599b20&chksm=e8b514d1dfc29dc73c235128913d1dc3756d9ab18d45b982b54eabcb13aa0b5c4c6281b62d2b#rd "微信公众号「码上花甲」"),同时发布到[掘金](https://juejin.cn/post/7285608128039960631 "稀土掘金"),欢迎关注。 近期社区中不少工具放弃了 TypeScript 转而用上了 JsDoc。 咱不评价这种做法是否值得推荐,但是,有一些项目从一开始就没有使用 TypeScript。这种情况下直接迁移到 TypeScript 成本会很高,那能否用 JsDoc 来“代替” TypeScript 呢? 答案是可行的。 我们不能因为 TS 而一定要用 TS,必须要结合项目实际情况来确定。 不过 JsDoc 也有很多细节,是你可能没注意到的。除了静态类型检查之外,JsDoc 的能力还是蛮强大的。 今天,我们就结合 TS 来看下 JsDoc 都能干哪些事情。 ## 1. 变量的类型 在 TS 中,要给一个变量定义类型很简单: ```tsx let name: string = 'Randal'; ``` 使用 TS,直接在变量 `name` 后面定义为 `string` 类型即可。 JsDoc 也能做到: ```jsx /** @type {string} */ let name = 'Randal'; ``` 编辑器同样可以识别出 JsDoc 注释的变量类型:  像其他基本类型,JsDoc 也都支持,比如 `number` 类型。 ```jsx /** @type {number} */ let age = 30; ``` 除了上面这种简单的基本类型之外,JsDoc 还支持联合类型。在 TS 中的写法是这样的: ```jsx const personName: 'Randal' | 'Olive' | 'Jack' = 'Randal'; ``` JsDoc 的写法稍微麻烦一点,但也能很好的支持: ```jsx /** * @typedef {'Randal' | 'Olive' | 'Jack'} PersonName */ /** * @type {PersonName} */ const personName = 'Randal'; ``` 同样的,编辑器也能正确识别出对应的类型:  对于普通的变量,在 JsDoc 中可以用 `@type` 标签描述变量的类型;对于联合类型可借助 `@typeof` 标签辅助完成。其他的基本类型写法都差不多,就不一一展示了。 ## 2. 函数参数和返回值 函数是 JS 中的一等公民,其重要性不言而喻。我们常见的类型定义一般都见于函数声明。 要使用 JsDoc 给函数参数定义类型,需要用到 `param` 标签。 比如下面的函数,用 TS 表示是这样的: ```tsx // Say hi to someone. function sayHiSomeone(name: string) { console.log('Hi ' + name); } ``` 很简单,写法类似于给变量定义类型,直接在函数参数后面给定相应的类型即可,上面的是 `string` 类型。 JsDoc 的话不难,只不过需要稍微多写一点注释: ```jsx /** * Say hi to someone. * @param {string} name - the name of somebody(with type and description) */ function sayHiSomeone(name) { console.log('Hi ' + name); } ``` `@param` 标签就表示给函数的参数定义类型和描述,`{}` 中的就是参数 `name` 的类型,后面的字符串是该参数的描述信息。 鼠标悬停在函数上,会显示函数的类型定义:  而函数的返回值被隐式地定义为了 `void` 类型。 也对,函数体内确实没有 `return` 数据出去,说明推断的是正确的。 如果要显式地定义函数返回值该怎么做呢? ```tsx /** * Say hi to someone. * @param {string} name - the name of somebody(with type and description) * @return {undefined} */ function sayHiSomeone(name) { console.log('Hi ' + name); return undefined; } ``` 可以用 `@return` 标签表示函数的返回值,`{}` 中定义函数返回值类型:  上面演示的都是简单类型的定义,对于稍微复杂一点的——比如对象类型或数组类型该怎么办呢? 下面是一个 TS 示例: ```tsx interface EmployeeType { name: string; department: string; } function Project() {} Project.prototype.assignment = function(employee: EmployeeType) { console.log(employee.name + ' works in ' + employee.department); }; ``` 借助 TS 的 `interface` 很容易就能定义参数 `employee` 的类型,在 JsDoc 中的写法却有点不一样: ```jsx function Project() {} /** * 记录参数属性 * @param {Object} employee - the employee for assignment * @param {string} employee.name - the name of employee * @param {string} employee.department - the department of employee */ Project.prototype.assignment = function(employee) { console.log(employee.name + ' works in ' + employee.department); }; ``` 可以看到第一个 `@param` 标签的类型是 `Object`,表示 `employee` 参数是一个对象类型。 然后,再以此定义这个对象类型中成员的类型,第二个和第三个 `@param` 标签定义的都是 `string` 类型。 不过,这里要注意的一点是,`@param` 标签类型后的 `employee` 必须要保持和函数参数名一致,编辑器才能识别出来。  数组参数类型的写法是差不多的,比如: ```jsx /** * 记录数组中值的属性 * @param {Object[]} employees - the employees params * @param {string} employees[].name - the name of employee * @param {string} employees[].department - the department of employee */ Project.prototype.employees = function(employees) { console.log(employees[0].name + ' works in ' + employees[0].department); }; ```  还有一种常见的场景,JsDoc 如何表示函数的参数是可选的呢?JsDoc 也考虑到这种情况了,用 `[]` 将参数名包裹起来就表示该参数是可选的: ```jsx /** * 可选参数 * @param {string} [somebody] - somebody's name */ function sayHello(somebody) { if (!somebody) { somebody = 'Randal Wang'; } console.log('Hello ' + (somebody || 'me')); } ``` 如果参数 `somebody` 没有定义,那么其类型应该是 `undefined`:  最后,一种常见的场景式函数参数是一个回调函数。回调函数在函数体内被调用: ```jsx /** * 回调函数 * @param {requestCallback} cb - the callback that handles the response */ function callback(cb) { if (typeof cb === 'function') { cb(); } } ``` 鼠标悬浮在函数上的效果如下:  ## 3. `async` `async` 函数表示该函数是异步的,比如在一个函数体内返回 `Promise`。用 JsDoc 来描述也不难: ```jsx /** * Download data from specitic url * @async * @param {string} url - the url to download data * @return {Promise<string>} the data from url */ async function downloadData(url) { return new Promise((resolve, _reject) => { setTimeout(() => { resolve(`Data from ${url}`); }, 1000); }); } ``` 效果如下:  ## 4. JsDoc 其他标签 除了上面提到的这几个标签较为常用之外,JsDoc 还提供了很多丰富、使用的标签,比如 `@see`、`@link`、`@abstract`、`@description`、`@todo` 等等。 虽然较之 TS 的能力还有不少差距,但是也已经能覆盖大部分场景了。 JsDoc 最大的特点是不会侵入到业务代码中,它是以正常的注释的形式存在的。要知道,我们即便用 TS 也是要写注释的。 所以,如果你的项目本来就是用 JS 写的,不妨先把 JsDoc 用起来。将来有机会的时候,再将其迁移到 TS 不失为一个好的选择。 上面仅仅提供几个代码片段,作为抛砖引玉吧算是。如果你对 JsDoc 其他标签还不熟悉,可以去看看官网的示例,很全面。 也可以参考和今天内容配套的源码,有一些常用标签的代码示例供你参考。 好了,今天的内容就先到这里吧,感谢阅读,下期再见 ;-) --- 为便于理解,为大家准备了源码以供参考: ```bash $ npm install -g @agelesscoding/cli # 安装公众号配套的 agc 脚手架 $ agc init # 在列表中,选择对应的源码示例,比如 20231001 表示 2023 年 10 月 01 号的文章源码 ``` Last modification:October 9, 2023 © Allow specification reprint Support Appreciate the author AliPayWeChat Like 4 如果觉得我的文章对你有用,请随意赞赏
One comment
adassd