公共模块,通常会被多个项目、不同的开发人员使用,所以开发公共模块时,你自己会用还不够,要让所有人都能很快的知道怎么去使用,这一点很关键。通常会从3个方面做到这点:
- 精心分割代码逻辑,遵循开闭原则;
- 变量名采用自解释性的标识符;
- 依赖完善的使用提示。
第1点用户体验不到,如果没有做好第3点,第2点用户也是体验不到的,只有第3点时真正面对用户的,所以做好第3点很重要!本篇文章就是教你如何在实现js模块时,做好完善的使用提示。
是否要用ts写公共项目?
目前不建议使用ts开发公共组件,理由如下:
你的项目正常应该是打包成 umd包,并且和源码一起发布。因为:
ts的很多优点,但的确不太适合开发公共项目,而且运用js Doc 注释,ts的绝大多数有点也能轻松实现。
而且即便业务项目使用ts开发,也能引用你的js模块。
为了不给业务项目带来副作用,就采用js语言吧。
js中使用ts
尽管确定使用 js语言编写公共项目,你依然享受到 ts的便利。ts天生就支持js!
// @ts-check
// js文件开头,使用上面这个注释,开启ts支持。接下来整个文件,都有了ts校验的功能,会出现类型错误提示等。 let a = 22
// @ts-ignore
a="string" // 将一个string赋给number类型的a,正常情况会有类型报错。但
// 上一行 // @ts-ignore 这个注释表示,接下来的一条语句忽略ts校验,所以这里不报错
开启 ts 校验后,接下来就使用 jsDoc了,如果你还安装了typeScript:
#全局安装:
npm install -g typescript #或 项目局部安装:
npm install --save-dev typescript
那么接下来,使用命令 : tsc --declaration src/lib/service.js --allowJs 就可以根据你所写的 JSDoc,为你的service.js文件生成 .d.ts文件了。
转换的示例(下方示例的js代码生成的.d.ts):
export type MethodType = "get" | "post" | "delete" | "put" | "patch";
export type RequesFn = (method: MethodType, url: string, params: any) => Promise<object>;
export type Person = {
name: string;
age: number;
sex: 'male' | 'female';
};
export type AxiosRes<T> = {
code: number;
data: T;
msg: string;
}; export type TokenRes = {
mediaId: string;
mediaKey: string;
token: string;
};
JSDoc示例
1、定义复杂类型+对变量添加类型
// @ts-check /**
* @typedef {{name:string,age:number,sex:'male'|'female'}} Person
*/
/**
* @type Person
*/
let jim = {name:'jim',age:28,sex:'female'}
把鼠标移到 jim变量上,还有类型提示,就像ts中一样:
关于类型提示的示例截图,接下来就不贴上了,你可以在 vscode 试一下。
2、函数的类型
/**
* @typedef {'get'|'post'|'delete'|'put'|'patch'} MethodType
*/
/**
* @returns { Promise<object>} // 定义返回的类型
* @param {MethodType} method // 注意是@param,不再是@type了,MethodType是前面定义好的类型
* @param {string} url
* @param {*} [params] // 中括号表示可选参数,*表示any类型
*/
function myAjax(method,url,params){
return new Promise(resolve=>{
// other code ...
})
}
js里也有类型推断,如果不指明返回的是 Promise<object> 这里会自动推断出返回的是 Promise<any>
你也可以这样定义函数类型:
/**
* @typedef {(method:MethodType,url:string,params:any)=>Promise<object>} RequesFn
*/
// 或
/**
* @typedef {{(method:MethodType,url:string,params:any):Promise<object>}} RequesFn
*/ // 使用:
/**
* @type {RequesFn}
*/
function myAjax(method, url, params) {
}
注意两种定义函数类型类型 RequestFn 的方式,和 ts 是完全一样的!
3、定义和使用泛型
/**
* @template T // 这里用 @template声明泛型类型T
* @typedef {{code:number,data:T,msg:string}} AxiosRes
*/ /**
* @typedef {{mediaId:string, mediaKey:string, token:string }} TokenRes
* @type {TokenRes|AxiosRes<TokenRes>} // 这里是泛型的使用
*/
let res
鼠标放到res变量上:
记住这个res,它是个复合类型的变量,下一节的类型强转就用它了
4、类型的强转(即ts中的类型断言)
if (/**@type {AxiosRes<TokenRes>} */(res).data === undefined) {
console.log('res 是TokenRes类型')
let mediaId = /**@type{TokenRes} */(res).mediaId
} else {
console.log('res 是AxiosRes<TokenRes>类型')
let mediaId = /**@type {AxiosRes<TokenRes>} */(res).data.mediaId
}
把注释去掉,其实很简单:
if (res.data === undefined) {
console.log('res 是TokenRes类型')
let mediaId = res.mediaId
} else {
console.log('res 是AxiosRes<TokenRes>类型')
let mediaId = res.data.mediaId
}
类型断言的方法就是 @type 注释后面将要断言的变量用括号括起来 /**@type{TokenRes} */(res)
更多示例,我就不列举了,可以查看官网:https://www.html.cn/doc/jsdoc/about-namepaths.html 自己学习了
加上jsDoc的好处
回到最开始的话题,你的公共项目如何给使用者完善的使用提示?
当你使用jsDoc时,你会发现 业务项目如果引用你的源码,会自然的带上提示
如果是引用你的dist压缩包,那么我们合理的组织代码,然后用 tsc --declaration --allowJs xxx.js 命令,生成 .d.ts 发布出去,一样会有良好的提示。