• JsDoc和TypeScript的语法比较
  • 发布于 2个月前
  • 202 热度
    0 评论
近期社区中不少工具放弃了 TypeScript 转而用上了 JsDoc。咱不评价这种做法是否值得推荐,但是,有一些项目从一开始就没有使用 TypeScript。这种情况下直接迁移到 TypeScript 成本会很高,那能否用 JsDoc 来“代替” TypeScript 呢?

答案是可行的。

我们不能因为 TS 而一定要用 TS,必须要结合项目实际情况来确定。不过 JsDoc 也有很多细节,是你可能没注意到的。除了静态类型检查之外,JsDoc 的能力还是蛮强大的。

今天,我们就结合 TS 来看下 JsDoc 都能干哪些事情。

1. 变量的类型
在 TS 中,要给一个变量定义类型很简单:
let name: string = 'Randal';
使用 TS,直接在变量 name 后面定义为 string 类型即可。

JsDoc 也能做到:
/** @type {string} */
let name = 'Randal';
编辑器同样可以识别出 JsDoc 注释的变量类型:

像其他基本类型,JsDoc 也都支持,比如 number 类型。
/** @type {number} */
let age = 30;
除了上面这种简单的基本类型之外,JsDoc 还支持联合类型。在 TS 中的写法是这样的:
const personName: 'Randal' | 'Olive' | 'Jack' = 'Randal';
JsDoc 的写法稍微麻烦一点,但也能很好的支持:
/**
 * 堆代码 duidaima.com
 * @typedef {'Randal' | 'Olive' | 'Jack'} PersonName
 */

/**
 * @type {PersonName}
 */
const personName = 'Randal';
同样的,编辑器也能正确识别出对应的类型:

对于普通的变量,在 JsDoc 中可以用 @type 标签描述变量的类型;对于联合类型可借助 @typeof 标签辅助完成。其他的基本类型写法都差不多,就不一一展示了。

2. 函数参数和返回值
函数是 JS 中的一等公民,其重要性不言而喻。我们常见的类型定义一般都见于函数声明。要使用 JsDoc 给函数参数定义类型,需要用到 param 标签。
比如下面的函数,用 TS 表示是这样的:
// Say hi to someone.
function sayHiSomeone(name: string) {
  console.log('Hi ' + name);
}
很简单,写法类似于给变量定义类型,直接在函数参数后面给定相应的类型即可,上面的是 string 类型。
JsDoc 的话不难,只不过需要稍微多写一点注释:
/**
 * 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 数据出去,说明推断的是正确的。
如果要显式地定义函数返回值该怎么做呢?
/**
 * 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 示例:
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 中的写法却有点不一样:
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 必须要保持和函数参数名一致,编辑器才能识别出来。

数组参数类型的写法是差不多的,比如:
/**
 * 记录数组中值的属性
 * @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 也考虑到这种情况了,用 [] 将参数名包裹起来就表示该参数是可选的:
/** 
 * 可选参数
 * @param {string} [somebody] - somebody's name
 */
function sayHello(somebody) {
  if (!somebody) {
    somebody = 'Randal Wang';
  }
  console.log('Hello ' + (somebody || 'me'));
}
如果参数 somebody 没有定义,那么其类型应该是 undefined:

最后,一种常见的场景式函数参数是一个回调函数。回调函数在函数体内被调用:
/**
 * 回调函数
 * @param {requestCallback} cb - the callback that handles the response
 */
function callback(cb) {
  if (typeof cb === 'function') {
    cb();
  }
}
鼠标悬浮在函数上的效果如下:

3. async
async 函数表示该函数是异步的,比如在一个函数体内返回 Promise。用 JsDoc 来描述也不难:
/**
 * 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 其他标签还不熟悉,可以去看看官网的示例,很全面。

好了,今天的内容就先到这里吧,感谢阅读,下期再见 ;-)
用户评论