1. 背景
现在的公共库用js来书写,有两个问题:
调用方无法获得语法提示,且对调用函数的参数类型不明
公共库中的一些变量类型模糊不清
以上两个问题虽然不大,但是一定程度上影响了项目的开发效率和质量。通过typescript来改写公共库,有利于解决上述两个问题。
2. 操作步骤
2.1 命令行安装Typescript
vue项目如果要想使用ts,需要先安装typescript和@vue/cli-plugin-typescript
1
2
| npm install --save-dev typescript
npm install --save-dev @vue/cli-plugin-typescript
|
2.2 编写typescript配置
在根目录下新建文件tsconfig.json,以下边的内容为例
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
| {
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"allowJs": false,
"noEmit": true,
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"exclude": [
"node_modules"
]
}
|
2.3 新增shims-vue.d.ts
如果调用方是vue工程,且需要在vue文件里写ts,需要加上这一步。
根目录下新建文件shims-vue.d.ts
,让 ts
识别 *.vue
文件
1
2
3
4
| declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
|
如果vue文件的script段需要用ts来写,需要这一步操作。由于我这次是改写公共js库,所以暂时跟这里不相关。
2.4 修改公共库文件后缀
将js文件改为ts文件后缀。这个时候会报很多错误,大部分是类型不匹配相关的,这里需要逐一解决。
3. 转ts代码时遇到的问题和改写方法
3.1 类型“{}”上不存在属性“get”
1
2
3
4
| var Cookies = {}
Cookies.get = function(key, defaultval) {
//省略若干代码
}
|
.get方法的定义会报错,这里是js转ts最常见的错误,解决方法是将Cookies定义为class
1
2
3
4
5
| class Cookies {
static get(key: string, defaultval?: string) {
//省略若干代码
}
}
|
这里需要将get方法定义成static,这样才可以以Cookies.get
的形式调用,和之前保持不变。
如果要使用的对象{}里只包含属性,不包含方法,也可以将{}定义为接口interface,例如
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
| interface Env{
ua: string;
currPage: string;
isWeixin: boolean;
isWorkWeixin: boolean;
isQQ: boolean;
isPvpApp: boolean;
isTipApp: boolean;
isAndroid: boolean;
isIos: boolean;
isMsdk: boolean;
isSlugSdk: boolean;
isInGame: boolean;
isGHelper: boolean;
isGHelper20004: boolean;
isMiniProgram: boolean;
isWindowsPhone: boolean;
isSymbian: boolean;
isPc: boolean;
version: string;
loginType: string;
authQuery: Object;
urlmodule: string;
vconsole: any;
}
|
使用的时候如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| var env = {} as Env;
env.ua = useragent.toLowerCase()
env.isWeixin = env.ua.indexOf('micromessenger') != -1
env.isWorkWeixin = env.ua.indexOf('micromessenger') != -1 && env.ua.indexOf('wxwork') != -1
env.isQQ = env.ua.indexOf(' qq/') != -1
env.isPvpApp = env.ua.indexOf(' igameapp/') != -1
env.isTipApp = env.ua.indexOf(' gamelife/') != -1
env.isAndroid = env.ua.indexOf('android') != -1
env.isIos = (env.ua.indexOf('iphone') != -1) || (env.ua.indexOf('ipad') != -1)
env.isMsdk = env.ua.indexOf(' msdk/') != -1 // msdk
env.isSlugSdk = env.ua.indexOf('ingame') != -1 // 微社区sdk
env.isInGame = env.isMsdk || env.isSlugSdk // 是否游戏内
env.isGHelper = env.ua.indexOf('gamehelper') != -1
env.isGHelper20004 = env.ua.indexOf('gamehelper_20004') != -1
env.isMiniProgram = env.ua.match(/miniprogram/i) != null || window.__wxjs_environment === 'miniprogram'
|
3.2 typeof后边的内容未定义
1
2
3
| if (typeof node_cookie !== 'undefined') {
cookie = node_cookie
}
|
这里node_cookie是未定义的内容,可能是在其他环境里载入的变量,目前因为用不到,所以暂时是去掉了
3.3 window下的属性不存在
1
| window.__wxjs_environment === 'miniprogram'
|
这行代码会报错,类型“Window & typeof globalThis”上不存在属性“__wxjs_environment”,解决方法是声明一个Window下的全局变量
1
2
3
| declare global {
interface Window { __wxjs_environment: any; }
}
|
3.4 赋值时类型不匹配
3.4.1 第一个例子
1
| env.isMiniProgram = env.ua.match(/miniprogram/i)
|
不能将类型“RegExpMatchArray”分配给类型“boolean”。这里就是typescript的好处,会提示隐示的类型转换,避免可能出现的bug。字符串的match方法,可能返回Array或者null,所以这里的解决方案是改成
1
| env.isMiniProgram = env.ua.match(/miniprogram/i) != null
|
3.4.2 第二个例子
1
2
3
| let tmpd = new Date(new Date(timestamp).toLocaleDateString())// 今日0点
tmpd = Math.floor(tmpd.getTime() / 1000)
return tmpd + 86400 - 1
|
错误提示:不能将类型“number”分配给类型“Date”。出错在第2行,右边计算出来是number,但是tmpd是Date类型,出现类型不匹配,可以简单粗暴的将tmpd改为any类型
1
2
3
| let tmpd : any = new Date(new Date(timestamp).toLocaleDateString())// 今日0点
tmpd = Math.floor(tmpd.getTime() / 1000)
return tmpd + 86400 - 1
|
3.4.3 第三个例子
1
| var mistiming = Math.round(new Date() / 1000) - timestamp
|
错误提示:算术运算左侧必须是 “any"、"number"、"bigint” 或枚举类型。原因是Date类型和number类型做除法运算,需要修改为
1
| var mistiming = Math.round(new Date().getTime() / 1000) - timestamp
|
3.5 类型“Object”上不存在属性“tipmdl”
1
2
3
| if (typeof env.authQuery.tipmdl !== 'undefined' && env.authQuery.tipmdl != '') {
env.urlmodule = env.authQuery.tipmdl
}
|
这里authQuery是一个Object,里边存储了url的参数对,可能有任意key,所以修改成
1
2
3
| if (typeof env.authQuery['tipmdl'] !== 'undefined' && env.authQuery['tipmdl'] != '') {
env.urlmodule = env.authQuery['tipmdl']
}
|
4. 给typescript代码加上注释
1
2
3
4
5
6
7
8
9
10
| /**
* 设置cookie
* @param sName cookie名称
* @param sValue cookie值
* @param iExpireSec 过期时间,单位秒
* @param sDomain cookie作用域名
* @param sPath cookie作用路径
* @param bSecure 是否加密
*/
static set(sName:string, sValue:string, iExpireSec:number, sDomain:string, sPath:string, bSecure?:boolean)
|
像上面这样写好注释后,调用方就能看到了。如下
5. 参考资料