1
- # TypeScript 与 ESLint
1
+ # 代码检查
2
2
3
- [ ESLint ] [ ] 是一个代码检查工具,主要用来发现代码错误、统一代码风格,目前已被广泛的应用于各种 JavaScript 项目中 。
3
+ 目前 TypeScript 的代码检查主要有两个方案:使用 [ TSLint ] [ ] 或使用 [ ESLint ] [ ] + [ ` typescript-eslint-parser ` ] [ ] 。
4
4
5
- 它通过插件化的特性极大的丰富了适用范围,搭配 [ ` typescript-eslint-parser ` ] [ ] 之后,甚至可以用来检查 TypeScript 代码。
5
+ ## 什么是代码检查
6
6
7
- ## 为什么需要 ESLint
7
+ 代码检查主要是用来发现代码错误、统一代码风格。
8
8
9
- TypeScript 除了是一个编译 ts 文件的工具以外,还可以 [ 作为一个静态代码检查工具使用 ] ( ) 。
9
+ 在 JavaScript 项目中,我们一般使用 [ ESLint ] [ ] 来进行代码检查。它通过插件化的特性极大的丰富了适用范围,搭配 [ ` typescript-eslint-parser ` ] [ ] 之后,甚至可以用来检查 TypeScript 代码 。
10
10
11
- TypeScript 会对文件进行语法解析,如果遇到一些语法错误,或使用了未定义的变量,则会报错 。
11
+ [ TSLint ] [ ] 与 [ ESLint ] [ ] 类似,不过除了能检查常规的 js 代码风格之外,TSLint 还能够通过 TypeScript 的语法解析,利用类型系统做一些 ESLint 做不到的检查 。
12
12
13
- ESLint 也会对文件进行语法解析,它可以对一些代码风格进行约束,发现未定义的变量,但是对于错误的属性或方法引用,却无能为力。
13
+ ## 为什么需要代码检查
14
14
15
- 我们对同样一段代码分别运行 ` tsc ` 和 ` eslint ` ,会得到如下报错信息:
15
+ 有人会觉得,JavaScript 非常灵活,所以需要代码检查。而 TypeScript 已经能够在编译阶段检查出很多问题了,为什么还需要代码检查呢?
16
+
17
+ 因为 TypeScript 关注的重心是类型的匹配,而不是代码风格。当团队的人员越来越多时,同样的逻辑不同的人写出来可能会有很大的区别:
18
+
19
+ - 缩进应该是四个空格还是两个空格?
20
+ - 是否应该禁用 ` var ` ?
21
+ - 接口名是否应该以 ` I ` 开头?
22
+ - 是否应该强制使用 ` === ` 而不是 ` == ` ?
23
+
24
+ 这些问题都是 TypeScript 不会关注,但是却影响到多人协作开发时的效率、代码的可理解性、和可维护性。
25
+
26
+ 下面来看一个具体的例子:
16
27
17
28
``` ts
18
29
let myName = ' Tom' ;
@@ -27,6 +38,7 @@ console.log(`My name is ${myName}`)
27
38
// index.ts(4,34): error TS2551: Property 'toStrng' does not exist on type 'string'. Did you mean 'toString'?
28
39
//
29
40
//
41
+ //
30
42
// eslint 报错信息:
31
43
//
32
44
// /path/to/index.ts
@@ -35,29 +47,107 @@ console.log(`My name is ${myName}`)
35
47
//
36
48
// ✖ 2 problems (2 errors, 0 warnings)
37
49
// 1 errors, 0 warnings potentially fixable with the `--fix` option.
50
+ //
51
+ //
52
+ //
53
+ // tslint 报错信息:
54
+ //
55
+ // ERROR: /path/to/index.ts[5, 36]: Missing semicolon
56
+ ```
57
+
58
+ | 存在的问题 | ` tsc ` 是否报错 | ` eslint ` 是否报错 | ` tslint ` 是否报错 |
59
+ | --------- | ------------- | ---------------- | ----------------- |
60
+ | ` myName ` 被勿写成了 ` myNane ` | ✅ | ✅ | ❌ |
61
+ | ` toString ` 被勿写成了 ` toStrng ` | ✅️ | ❌ | ❌ |
62
+ | 少了一个分号 | ❌ | ✅ | ✅ |
63
+
64
+ 上例中,由于 ` eslint ` 和 ` tslint ` 均无法识别 ` myName ` 存在哪些方法,所以对于拼写错误的 ` toString ` 没有检查出来。
65
+
66
+ 而代码风格的错误不影响编译,故少了一个分号的错误 ` tsc ` 没有检查出来。
67
+
68
+ 对于未定义的变量 ` myNane ` ,` tsc ` 可以检测出来。由于用到 ` tslint ` 的地方肯定会接入 ` tsc ` 编译,所以 ` tslint ` 就没必要检测这个错误了。` eslint ` 需要能够独立于某个编译环境,所以能检测出此类错误,但是对于 TypeScript 代码,这其实是一种冗余的检测了。
69
+
70
+ 其实不止 ` tsc ` 与 ` eslint ` 之间有冗余的检测,` tsc ` 与 ` tslint ` 之间也有一些冗余的检测,但是大部分都是因为早期的 ` tsc ` 还没能做到检测此类错误。
71
+
72
+ 比如 TSLint 中的 ` typeof-compare ` 要求 ` typeof ` 表达式比较的对象必须是 ` 'undefined' ` , ` 'object' ` , ` 'boolean' ` , ` 'number' ` , ` 'string' ` , ` 'function' ` 或 ` 'symbol' ` 之一。而 TypeScript 2.2 之后,编译器就已经自带了这个功能。
73
+
74
+ 下图表示了 ` tsc ` , ` eslint ` 和 ` tslint ` 能覆盖的检查:
75
+
76
+ @TODO 图片待补充
77
+
78
+ 上图中,` tsc ` , ` eslint ` 和 ` tslint ` 之间互相都有重叠的部分,也有各自独立的部分。
79
+
80
+ 虽然发现代码错误比统一的代码风格更重要,但是当一个项目越来越庞大,开发人员也越来越多的时候,代码风格的约束还是必不可少的。
81
+
82
+ ## 应该使用哪种代码检查工具
83
+
84
+ TSLint 与 ESLint 作为检查 TypeScript 代码的工具,各自都有自己的优点:
85
+
86
+ TSLint 的优点:
87
+
88
+ 1 . 专为 TypeScript 服务,bug 比 ESLint 少
89
+ 2 . 不受限于 ESLint 使用的语法树 [ ESTree] ( https://github.com/estree/estree )
90
+ 3 . 能直接通过 ` tsconfig.json ` 中的配置编译整个项目,使得在一个文件中的类型定义能够联动到其他文件中的代码检查
91
+
92
+ ESLint 的优点:
93
+
94
+ 1 . 基础规则比 TSLint 多很多(249 : 151)
95
+ 2 . 社区繁荣,插件众多([ 50+] ( https://github.com/dustinspecker/awesome-eslint#plugins ) : 9)
96
+
97
+ 下面来看一些具体的例子:
98
+
99
+ ``` ts
100
+ let foo: string = 1 + ' 1' ;
101
+
102
+ // tslint 报错信息:
103
+ //
104
+ // ERROR: /path/to/index.ts[1, 19]: Operands of '+' operation must either be both strings or both numbers, consider using template literals
38
105
```
39
106
40
- | 存在的问题 | ` tsc ` 是否报错 | ` eslint ` 是否报错 |
41
- | --------- | ------------- | ---------------- |
42
- | ` myName ` 被勿写成了 ` myNane ` | ✅ | ✅ |
43
- | ` toString ` 被勿写成了 ` toStrng ` | ✅️ | ❌ |
44
- | 少了一个分号 | ❌ | ✅ |
107
+ 以上代码在 TSLint 中会报错,原因是加号两边必须同为数字或同为字符串(需要开启 ` restrict-plus-operands ` 规则)。
45
108
46
- 上例中,由于 ` eslint ` 无法识别 ` myName ` 存在哪些方法,所以对于拼写错误的 ` toString ` 没有检查出来。而代码风格的错误不影响编译,故 ` tsc ` 没有检查出来 。
109
+ ESLint 无法知道加号两边的类型,所以对这种规则无能为力 。
47
110
48
- 未定义的变量两者都能检查出来。
111
+ ``` ts
112
+ function foo(a , b , c , d , e , f , g , h ) {
113
+ doSomething ();
114
+ }
115
+
116
+ // eslint 报错信息:
117
+ //
118
+ // /path/to/index.ts
119
+ // 1:1 error Function 'foo' has too many parameters (8). Maximum allowed is 7 max-params
120
+ //
121
+ // ✖ 1 problem (1 error, 0 warnings)
122
+ ```
49
123
50
- 值得注意的是, ` tsc ` 不仅检查出来了代码问题,还非常智能的给出了修改建议 。
124
+ ESLint 可以检测出来以上代码的函数参数超过了 7 个(需要开启 ` max-params ` 规则) 。
51
125
52
- 下面是 TypeScirpt 作为一个静态代码检查工具,与 ESLint 的关系图:
126
+ 但是 TSLint 没有此项检查,虽然也可以实现,但是需要自己手动写一条规则。
53
127
54
- ![ TypeScript 和 ESLint 的关系 ] ( ../assets/typescript-eslint.png )
128
+ 那么到底该使用哪种代码检测工具呢?经过一些实践,我建议可以按照以下流程决定:
55
129
56
- 上图中,TypeScript 与 ESLint 有重叠的部分,也有各自独立的部分,虽然发现代码错误比统一的代码风格更重要,但是当一个项目越来越庞大,开发人员也越来越多的时候,代码风格的约束还是必不可少的。
130
+ ``` ts
131
+ if (' 项目正在由 js 改造为 ts 的过程中' ) {
132
+ if (' 原 js 项目使用了 eslint' ) {
133
+ if (' 将会长时间处于 js 与 ts 共存的状态' ) {
134
+ // 由于一个项目中无法针对不同后缀的文件使用不同的 eslint 配置
135
+ // 故新 ts 文件只能使用 tslint 检查,老 js 文件保持使用 eslint 检查
136
+ return ' tslint' & ' eslint' ;
137
+ } else if (' 能够快速的将所有文件都重构为 ts' ) {
138
+ // 使原有 eslint 支持对 ts 文件的检查
139
+ return ' eslint' ;
140
+ }
141
+ }
142
+ }
143
+ // TSLint 利用类型系统增强的一些检查大多是无关痛痒的,或者是可以在 tsc 编译过程中检查出来的
144
+ // ESLint 生态环境更好,虽然现在还存在一些 bug,但是应该积极使用,推动它的发展
145
+ return ' eslint' ;
146
+ ```
57
147
58
- 下面我们就来一步一步给 TypeScript 项目添加 ESLint 检查。
148
+ ## 在 TypeScript 中使用 ESLint
59
149
60
- ## 安装
150
+ ### 安装 ESLint
61
151
62
152
ESLint 可以安装在当前项目中或全局环境下,因为代码检查是项目的重要组成部分,所以我们一般会将它安装在当前项目中。可以运行下面的脚本来安装:
63
153
@@ -77,7 +167,7 @@ npm install typescript typescript-eslint-parser --save-dev
77
167
npm install eslint-plugin-typescript --save-dev
78
168
```
79
169
80
- ## 创建配置文件
170
+ ### 创建配置文件
81
171
82
172
ESLint 需要一个配置文件来决定对哪些规则进行检查,配置文件的名称一般是 ` .eslintrc.js ` 或 ` .eslintrc.json ` 。
83
173
@@ -118,7 +208,7 @@ module.exports = {
118
208
- 警告:代码检查时输出错误信息,但是不会影响到 exit code
119
209
- 报错:发现错误时,不仅会输出错误信息,而且 exit code 将被设为 1(一般 exit code 不为 0 则表示执行出现错误)
120
210
121
- ## 检查一个 ts 文件
211
+ ### 检查一个 ts 文件
122
212
123
213
创建了配置文件之后,我们来创建一个 ts 文件看看是否能用 ESLint 去检查它了。
124
214
@@ -172,7 +262,7 @@ if (tom.age == 25) {
172
262
173
263
这时只需执行 ` npm run eslint ` 即可。
174
264
175
- ## 检查整个项目的 ts 文件
265
+ ### 检查整个项目的 ts 文件
176
266
177
267
我们的项目源文件一般是放在 ` src ` 目录下,所以需要将 ` package.json ` 中的 ` eslint ` 脚本改为对一个目录进行检查。由于 ` eslint ` 默认不会检查 ` .ts ` 后缀的文件,所以需要加上参数 ` --ext .ts ` :
178
268
@@ -186,7 +276,7 @@ if (tom.age == 25) {
186
276
187
277
此时执行 ` npm run eslint ` 即会检查 ` src ` 目录下的所有 ` .ts ` 后缀的文件。
188
278
189
- ## 在 VSCode 中集成 ESLint 检查
279
+ ### 在 VSCode 中集成 ESLint 检查
190
280
191
281
在编辑器中集成 ESLint 检查,可以在开发过程中就发现错误,极大的增加了开发效率。
192
282
@@ -208,7 +298,7 @@ VSCode 中的 ESLint 插件默认是不会检查 `.ts` 后缀的,需要在「
208
298
209
299
![ VSCode ESLint 错误信息] ( ../assets/vscode-eslint-error.png )
210
300
211
- ## 使用已完善的配置
301
+ ### 使用已完善的配置
212
302
213
303
ESLint 原生的规则和 ` eslint-plugin-typescript ` 的规则太多了,而且原生的规则有一些在 TypeScript 中支持的不好,需要禁用掉。
214
304
@@ -250,17 +340,17 @@ module.exports = {
250
340
};
251
341
```
252
342
253
- ## 使用 ESLint 检查 tsx 文件
343
+ ### 使用 ESLint 检查 tsx 文件
254
344
255
345
如果需要同时支持对 tsx 文件的检查,则需要对以上步骤做一些调整:
256
346
257
- ### 安装 ` eslint-plugin-react `
347
+ #### 安装 ` eslint-plugin-react `
258
348
259
349
``` bash
260
350
npm install --save-dev eslint-plugin-react
261
351
```
262
352
263
- ### package.json 中的 scripts.eslint 添加 ` .tsx ` 后缀
353
+ #### package.json 中的 scripts.eslint 添加 ` .tsx ` 后缀
264
354
265
355
``` json
266
356
{
@@ -270,7 +360,7 @@ npm install --save-dev eslint-plugin-react
270
360
}
271
361
```
272
362
273
- ### VSCode 的配置中新增 typescriptreact 检查
363
+ #### VSCode 的配置中新增 typescriptreact 检查
274
364
275
365
``` json
276
366
{
@@ -283,10 +373,14 @@ npm install --save-dev eslint-plugin-react
283
373
}
284
374
```
285
375
286
- ### 使用 AlloyTeam ESLint 规则中的 TypeScript React 版本
376
+ #### 使用 AlloyTeam ESLint 规则中的 TypeScript React 版本
287
377
288
378
[ AlloyTeam ESLint 规则中的 TypeScript React 版本] ( https://github.com/AlloyTeam/eslint-config-alloy#typescript-react )
289
379
380
+ ## 在 TypeScirpt 中使用 TSLint
381
+
382
+ @TODO 待补充
383
+
290
384
## Troubleshootings
291
385
292
386
### Cannot find module 'typescript-eslint-parser'
@@ -297,6 +391,8 @@ npm install --save-dev eslint-plugin-react
297
391
298
392
需要关闭 ` eslint-plugin-react ` 中的规则 ` react/jsx-indent ` 。
299
393
394
+ 如果还不行,多半是因为某些规则需要被关闭,可以使用「二分排错法」检查是哪个规则造成了错误。也欢迎[ 给 eslint-config-alloy 提 issue] ( https://github.com/AlloyTeam/eslint-config-alloy/issues/new )
395
+
300
396
### VSCode 没有显示出 ESLint 的报错
301
397
302
398
1 . 检查「文件 => 首选项 => 设置」中有没有配置正确
@@ -308,5 +404,23 @@ npm install --save-dev eslint-plugin-react
308
404
309
405
![ VSCode 的 ESLint 输出] ( ../assets/vscode-output-eslint.png )
310
406
407
+ ### 为什么 ESLint 无法检查出使用了未定义的变量(` no-undef ` 规则为什么被关闭了)?
408
+
409
+ 因为 ` typescript-eslint-parser ` [ 无法支持 ` no-undef ` 规则] ( https://github.com/eslint/typescript-eslint-parser/issues/416 ) 。它针对正确的接口定义会报错。
410
+
411
+ ### 为什么有些定义了的变量(比如使用 ` enum ` 定义的变量)未使用,ESLint 却没有报错?
412
+
413
+ 因为无法支持这种变量定义的检查。建议在 ` tsconfig.json ` 中添加以下配置,使 ` tsc ` 编译过程能够检查出定义了未使用的变量:
414
+
415
+ ``` json
416
+ {
417
+ "compilerOptions" : {
418
+ "noUnusedLocals" : true ,
419
+ "noUnusedParameters" : true
420
+ }
421
+ }
422
+ ```
423
+
424
+ [ TSLint ] : https://palantir.github.io/tslint/
311
425
[ ESLint ] : https://eslint.org/
312
426
[ `typescript-eslint-parser` ] : https://github.com/eslint/typescript-eslint-parser
0 commit comments