背景
在对接外部api时,拿到的响应obj,往往结构极其复杂,各种嵌套,加上字段命名不规范,无法确定用途。在开发过程中,js数量超过200行后,没有类型提示,很快就忘了变量是干什么的了。这时候灾难性的后果就会显现:运行时出现类型错误,反复回忆变量含义,等等。
那么,引入类型就十分必要了。不过新的问题是,如何为一个现存项目引入类型呢?在这个场景中,项目是一个Nodejs server,所有代码都是js,用ts改写,成本太大,而且需要额外的编译步骤才能上线。还有除了个别复杂的api需要类型,大部分api的内部并不涉及太过复杂的逻辑。
jsdoc
ts官方文档有一个专题介绍在js中引入类型的,分三步走:
- 在js文件顶部添加
// @ts-check
(这一步不仅是为了作为jsdoc的前置条件,还能发现拼写错误、undefined、重复定义等问题) - 添加jsdoc注释
- 用ts重写js文件
@ts-check开启后,违背类型检查的代码才会被IDE报错。如果一些错误处你认为无需标出(比如引用了一个没有类型提示的外部库),你可以使用@ts-ignore来忽略错误。
jsdoc是注释形式添加类型的,这就带来了很大的灵活性。
📦 最常用的 JSDoc 语法
1. 给函数加注释
/**
* 加法函数
* @param {number} a 第一个数字
* @param {number} b 第二个数字
* @returns {number} 返回相加结果
*/
function add(a, b) {
return a + b;
}
2. 给变量加类型
/** @type {string} */
let name = "Lee";
/** @type {number[]} */
let scores = [90, 85, 100];
常用类型:
类型 | 写法 |
---|---|
数字 | number |
字符串 | string |
布尔值 | boolean |
数组 | string[] 或 Array<string> |
对象 | Object 或 {foo: string, bar: number} |
函数 | function 或更详细签名见下 |
任意类型 | * 或 any |
可选参数 | @param {string} [name] |
默认值参数 | @param {string} [name="张三"] |
返回类型 | @returns {string} |
3. 自定义对象类型(接口)
/**
* @typedef {Object} User
* @property {string} name 姓名
* @property {number} age 年龄
*/
/**
* 打印用户信息
* @param {User} user
*/
function printUser(user) {
console.log(`${user.name}, ${user.age}`);
}
4. 给函数签名加类型
/**
* @param {(a: number, b: number) => number} fn - 接收一个函数参数
*/
function calc(fn) {
return fn(1, 2);
}
把jsdoc放在单独的文件里
独立文件有两种方法,一种是配合tsconfig放在dts,一种是放在js文件。我认为js文件更适合,因为引入ts会让项目结构变得很奇怪。创建一个types.js
文件,比如:
/**
* @typedef {Object} User
* @property {string} name 姓名
* @property {number} age 年龄
*/
然后在index.js
中引入:
import './types.js' // 运行时不会导入
/**
* @param {User} user
*/
const user = {
name: '张三',
age: 18
}
总结
jsdoc是属于ts生态系统内的类型工具,借助vscode等IDE,以低成本享受类型便利。对于复杂的代码,可以按需引入,而不影响整体结构。唯一缺点可能是语法比typescript啰嗦一点。
Content licenced under CC BY-NC-ND 4.0