正文
背景
建立的代码规范没人遵守,项目中遍地风格迥异的代码,你会不会抓狂?
通过测试用例的程序还会出现Bug,而原因仅仅是自己犯下的低级错误,你会不会抓狂?
某种代码写法存在问题导致崩溃时,只能全工程检查代码,这需要人工花费大量时间Review代码,你会不会抓狂?
以上这些问题,可以通过静态检查有效地缓解!
静态检查(Static Program Analysis)主要是以不运行程序的方式对于程序源代码进行检查分析的技术,而与之相反的就是动态检查(Dynamic Program Analysis),通过实际运行程序输入测试数据产生预期结果的技术。通过代码静态检查,我们可以快速定位代码的错误与缺陷,可以减少逐行阅读代码浪费的时间,可以(根据需要)快速扫描代码中可能存在的漏洞等。代码静态检查可以在代码的规范性、安全性、可靠性、可维护性等方面起到重要作用。
在客户端中,Android可以使用CheckStyle、Lint、Findbugs、PMD等工具,iOS可以使用Clang Static Analyzer、OCLint等工具。而在React Native的开发过程中,针对于JavaScript的ESLint,与TypeScript的TSLint,则成为了主要代码静态检查的工具。本文将按照使用TSLint的原因、使用TSLint的方法、自定义TSLint的步骤进行探究分析。
一、使用TSLint的原因
在客户端团队进入React Native项目的开发过程中,面临着如下问题:
-
由于大家从客户端转入到React Native开发过程中,容易出现低级语法错误;
-
开发者之前从事Android、iOS、前端等工作,因此代码风格不同,导致项目代码风格不统一;
-
客户端效果不一致,有可能Android端显示正常、iOS端显示异常,或者相反的情况出现。
虽然以上问题可以通过多次不断将雷点标记出,并不断地分享经验与强化代码Review过程等方式来进行缓解,但是仍面临着React Native开发者掌握的技术水平千差万别,知识分享传播的速度缓慢等问题,既导致了开发成本的不断增加和开发效率持续低下的问题,还难以避免一个坑被踩了多次的情况出现。这时急需一款可以满足以下目标的工具:
-
可检测代码低级语法错误;
-
规范项目代码风格;
-
根据需要可自定义检查代码的逻辑;
-
工具使用者可以“傻瓜式”的接入部署到开发IDE环境;
-
可以快速高效地将检查工具最新检查逻辑同步到开发IDE环境中;
-
对于检查出的问题可以快速定位。
根据上述要求的描述,静态检查工具TSLint可以较为有效地达成目标。
二、TSLint介绍
TSLint是硅谷企业Palantir的一个项目,它是一款可以检查TypeScript代码可读性、可维护性以及功能性错误的静态检查工具,当前许多编辑器(Editors)和构建系统(Build Systems)支持这一工具,同时支持自定义编写Lint规则、配置、格式化等。
当前TSLint已经包含了上百条规则,这些规则构筑了当前TSLint检查的基础。在代码开发阶段中,通过这些配置好的规则可以给工程一个完整的检查,并随时可以提示出可能存在的问题。本文内容参考了TSLint官方文档
https://palantir.github.io/tslint/
。
2.1 TSLint常见规则
以下规则主要来源于TSLint规则,是某些规则的简单介绍。
TSLint规则示例
2.2 常用TSLint规则包
上述2.1所列出的规则来源于Palantir官方TSLint规则。实际还有多种,可能会用到的有以下:
TSLint规则示例
我们在项目的规则配置过程中,一般采用上述规则包其中一种或者若干种同时配置,那如何配置呢?请看下文。
三、如何进行TSLint规则配置与检查
首先,在工程package.json文件中配置TSLint包:
TSLint规则示例
在根目录中的tslint.json文件中可以根据需要配置已有规则,例如:
TSLint规则示例
其中extends数组内放置继承的TSLint规则包,上图包括了airbnb配置的规则包、tslint-react的规则包,而rules用于配置规则的开关。
TSLint规则目前只有true和false的选项,这导致了结果要么正常,要么报错ERROR,而不会出现WARNING等警告。
有些时候,虽然配置某些规则开启,但是某个文件内可能会关闭某些甚至全部规则检查,这时候可以通过规则注释来配置,如:
/* tslint:disable */
上述注释表示本文件自此注释所在行开始,以下的所有区域关闭TSLint规则检查。
/* tslint:enable */
上述注释表示本文件自此注释所在行开始,以下的所有区域开启TSLint规则检查。
/* tslint:disable:rule1 rule2 rule3... */
上述注释表示本文件自此注释所在行开始,以下的所有区域关闭规则rule1 rule2 rule3…的检查。
/* tslint:enable:rule1 rule2 rule3... */
上述注释表示本文件自此注释所在行开始,以下的所有区域开启规则rule1 rule2 rule3…的检查。
// tslint:disable-next-line
上述注释表示此注释所在行的下一行关闭TSLint规则检查。
someCode(); // tslint:disable-line
上述注释表示此注释所在行关闭TSLint规则检查。
// tslint:disable-next-line:rule1 rule2 rule3...
上述注释表示此注释所在行的下一行关闭规则rule1 rule2 rule3…的检查检查。
以上配置信息,这里具体参考了
https://palantir.github.io/tslint/usage/rule-flags/
。
3.1 本地检查
在完成工程配置后,需要下载所需要依赖包,要在工程所在根目录使用
npm install
命令完成下载依赖包。
IDE环境提示
在完成下载依赖包后,IDE环境可以根据对应配置文件进行提示,可以实时地提示出存在问题代码的错误信息,以VSCode为例:
TSLint规则示例
本地命令检查
VSCode目前还有继续完善的空间,如果部分文件未在窗口打开的情况下,可能存在其中错误未提示出的情况,这时候,我们可以通过本地命令进行全工程的检查,在React Native工程的根目录下,通过以下命令行执行:
tslint --project tsconfig.json --config tslint.json
(此命令如果不正确运行,可在之前加入./node_modules/.bin/)即为:
./node_modules/.bin/tslint --project tsconfig.json --config tslint.json
从而会提示出类似以下错误的信息:
src/Components/test.ts[1, 7]: Class name must be in pascal case
3.2 在线CI检查
本地进行代码检查的过程也会存在被人遗忘的可能性,通过技术的保障,可以避免人为遗忘,作为代码提交的标准流程,通过CI检查后再合并代码,可以有效避免代码错误的问题。CI系统可以为理解为一个云端的环境,环境配置与本地一致,在这种情况下,可以生成与本地一致的报告,在美团内部可以使用基于Jenkins的Castle CI系统, 生成结果与本地结果一致:
TSLint规则示例
3.3 其他方式
代码检查不止局限上述阶段,在代码commit、pull request、打包等阶段均可触发。
-
代码commit阶段,通过Hook方式可以触发代码检查,可以有效地将在线CI检查阶段强制提前,基本保证了在线CI检查的完全正确性。
-
代码pull request阶段,通过在线CI检查可以触发代码检查,可以有效保证合入分支尤其是主分支的正确性。
-
代码打包阶段,通过在线CI检查可以触发代码检查,可以有效保证打包代码的正确性。
四、自定义编写TSLint规则
4.1 为什么要自定义TSLint规则
当前的TSLint规则虽然涵盖了比较普遍问题的一些代码检查,但是实践中还是存在一些问题的:
-
团队中的个性化需求难以满足。例如,saga中的异步函数需要在最外层加try-catch,且catch块中需要加异常上报,这个明显在官方的TSLint规则无法实现,为此需要自定义的开发。
-
官方规则的开启与配置不符合当前团队情况。
基于以上原因其他团队也有自定义TSLint的先例,例如上文提到的tslint-microsoft-contrib、tslint-eslint-rules等。
4.2 自定义规则步骤
那自定义TSLint大概需要什么步骤呢,首先规则文件根据规范进行按部就班的编写规则信息,然后根据代码检查逻辑对语法树进行分析并编写逻辑代码,这也是自定义规则的核心部分了,最后就是自定义规则的使用了。
TSLint规则示例
自定义规则的示例直接参考官方的规则是最直接的,我们能这里参考一个比较简单的规则”class-name”。
“class-name”规则上文已经提到,它的意思是对类命名进行规范,当团队中类相关的命名不规范,会导致项目代码风格不统一甚至其他出现的问题,而”class-name”规则可以有效解决这个问题。我们可以看下具体的源码文件:
https://github.com/palantir/tslint/blob/master/src/rules/classNameRule.ts
。
然后将分步对此自定义规则进行讲解。
TSLint规则示例
第一步,文件命名
TSLint规则示例
规则命名必须是符合以下2个规则:
-
驼峰命名。
-
以’Rule’为后缀。
第二步,类命名
规则的类名是
Rule
,并且要继承
Lint.Rules.AbstractRule
这个类型,当然也可能有继承
TypedRule
这个类的时候,但是我们通过阅读源码发现,其实它也是继承自
Lint.Rules.AbstractRule
这个类。