利用 VSCode、JSDoc、d.ts 让你的代码更健壮

javascript 作为弱类型的语言,开发时不需要复杂的类型定义,可以快速实现需求完成业务迭代。但当项目越来越大,周期越来越长,弱类型反而会使得开发成本变高,利用JSDoc + vscode 可以一定程度上解决这个问题。

弱类型造成的问题

为什么说项目越来越大,周期越来越长,弱类型反而会使得开发成本变高呢?我的一个切身体会就是当一个项目变大,周期变长时,这个项目所要处理的数据也会越来越多越来越复杂。某个函数可能接收一个对象,这个对象里面都需要哪些参数呢?会返回哪些内容呢?事情在我们进行数据整理时越发变得复杂,有没有这个字段?是不是这样拼写的?大量的时间都浪费在了回忆记忆数据格式的上面。

比如我们接手了个项目,他有一个类似这样的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
function formatAudio(list, info, type = 'author') {
let isTypeA = type === 'A';
let isTypeB = type === 'B';
let name = isTypeA ? info.aaa.name : '';
let site = isTypeA ? info.bbb.short_name : info.short_name;
let someId = isTypeA ? info.album.id : '';

let formatList = list.map(item => {
if (item.itemData) {
item = item.itemData;
}
name = isTypeB ? item.name : isTypeA
? name
: item.announcer_name.length > 0 ? item.announcer_name : '未知';

someId = isTypeB ? item.someId : isTypeA
? someId
: item.albumInfo.length > 0 ? item.albumInfo[0].albums_id : '0';
let someID = someId;
site = isTypeB ? item.site : site;
let itemId = isTypeB ? item.itemId : item.id;
let itemURL = isTypeB ? item.itemURL : item.play_url;
let authorName = name;
let thumbnail = isTypeB ? item.thumbnail : item.image_url;
let title = item.title;
let duration = isTypeB ? item.some_time : item.time;

return {
someID,
itemURL,
name,
authorName,
itemId,
thumbnail,
site,
title,
someId,
duration
};
});

return formatList;

}

是不是一脸懵逼,这 list 里面到底有啥?整理数据的函数迭代太多次经常就会搞这个样子(并且写成这个样子的原因很可能也是因为后面迭代的同学不知道具体数据格式到底是啥)。

并且,项目时间久了,开发者可能对之前的数据格式不熟悉了,甚至开发者都不是之前的人了。

详细的接口文档和代码注释是必要的,可以在一定程度上解决这个问题。但维护接口文档在项目迭代中是十分繁重的工作,并且也并不能更好的解决问题,我们需要一边翻文档一边写代码,还得祈祷写文档的同学没有写错某个字母,自己没有手残敲错某个字母,如果有错就需要在执行的时候才能发现了。

一个解决方法

这种情况,使用 typescript 或者 Flow 可以很好的解决,但是引入 typescript 和 Flow 的成本是很高的。一方面旧项目改造起来工作量很大,另一方面对团队来说有学习成本。

那么这时候 vscode 配合 JSDoc + d.ts 就成了一个比较好的解决方法。

vscode

vscode 大家都不陌生了,在前端开发中的占比越来越高,由于和 typescript 一样都为微软推出的,因此 vscode 天生对 typescript 有着很好的支持,JSDoc 和 d.ts 其实也都依赖 vscode 对 typescript 的支持。

JSDoc

JSDoc 的介绍可以看这里:http://usejsdoc.org/,它是用来根据注释产生 api 文档的生成器。

vscode 可以对 JSDoc 进行识别,来判断变量的类型,比如给变量指定一个类型:

1
2
/** @type {{a: string, b: number}} */
const var1;

定义一个复杂的类型:

1
2
3
4
5
6
7
8
9
10
/**
* @typedef {Object} SpecialType - creates a new type named 'SpecialType'
* @property {string} prop1 - a string property of SpecialType
* @property {number} prop2 - a number property of SpecialType
* @property {number=} prop3 - an optional number property of SpecialType
* @prop {number} [prop4] - an optional number property of SpecialType
* @prop {number} [prop5=42] - an optional number property of SpecialType with default
*/
/** @type {SpecialType} */
var specialTypeObject;

定义一个函数的参数和返回值:

1
2
3
4
5
6
7
8
9
10
11
/**
* @param p0 {string} - A string param declared using TS-style
* @param {string} p1 - A string param.
* @param {string=} p2 - An optional param
* @param {string} [p3] - Another optional param.
* @param {string} [p4="test"] - An optional param with a default value
* @return {string} This is the result
*/
function fn3(p0, p1, p2, p3, p4){
// TODO
}

具体支持的使用方式可以参考官方文档:https://github.com/Microsoft/TypeScript/wiki/JSDoc-support-in-JavaScript

d.ts

对于不是我们本次修改要维护的文件,可以不动这个文件,只给它添加一个 d.ts 文件,例如原始文件叫 a.js,那么可以在相同路径下添加一个 a.d.ts 文件,vscode 就会自动识别 d.ts 文件中的内容。

那么 d.ts 是什么呢?它有点类似于 c++ 中的 .h 文件。原本是用在 typescript 中的,不过 JavaScript 文件也一样可以使用。

a.d.ts 文件定义了 a.js 对外暴露内容的类型。

比如对外暴露函数的类型:

1
export function aaa(a: string): number;

暴露一个特定格式的对象:

1
2
3
4
5
6
interface SomeType {
id: number;
content: string;
}

export const a:SomeType;

具体功能可以查看:https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html

有什么好处?

说了半天 JSDoc 和 d.ts 只是让 vscode 能够识别代码中变量函数等的类型了,有什么好处呢?

好处就是 vscode 在编写代码阶段可以给我们强大的 代码提示代码补全以及代码检查

假设我们有一个 dFiles.js 文件:

1
2
3
4
5
6
7
8
export function someFunc(a) {
return 123;
}

export const a = {
id: 456,
content: 'content'
}

我们给它添加了一个 dFiles.d.ts 文件:

1
2
3
4
5
6
7
8
9
10
/** 这里可以添加一些函数注释 */
export function someFunc(a: string): number;

interface SomeType {
/** 这是一个id */
id: number;
content: string;
}

export const a:SomeType;

文件

代码提示:

代码提示

代码补全:

代码提示

如果我们在项目中添加一个 jsconfig.json 文件,还可以对 js 文件进行类型检查。

jsconfig 文件具体可以看这里:https://code.visualstudio.com/docs/languages/jsconfig

checkJs 字段设置为 true ,即可开启 js 的检查,通过 include 字段可以设置只检查特定文件。

例如,我们建立一个 jsconfig.json 文件,内容为:

1
2
3
4
5
6
7
8
9
{
"compilerOptions": {
"target": "es6",
"checkJs": true
},
"include": [
"index.js"
]
}

那么:

代码提示

可以看到由于 a 变量中只有 id 和 content,因此我们读取 a.index 就会进行报错。

总结

利用 vscode + JSDoc + d.ts,我们可以在开发截断给 js 添加类型检查和代码补全,从而有效增强我们的代码健壮性。

由于采用注释和增加文件的方法,渐进增强,因此有着较低的改造和学习成本。