创建的代码规范没人遵照,项目中遍地风格迥异的代码,你会不会抓狂?javascript
经过测试用例的程序还会出现Bug,而缘由仅仅是本身犯下的低级错误,你会不会抓狂?前端
某种代码写法存在问题致使崩溃时,只能全工程检查代码,这须要人工花费大量时间Review代码,你会不会抓狂?java
以上这些问题,能够经过静态检查有效地缓解!node
静态检查(Static Program Analysis)主要是以不运行程序的方式对于程序源代码进行检查分析的技术,而与之相反的就是动态检查(Dynamic Program Analysis),经过实际运行程序输入测试数据产生预期结果的技术。经过代码静态检查,咱们能够快速定位代码的错误与缺陷,能够减小逐行阅读代码浪费的时间,能够(根据须要)快速扫描代码中可能存在的漏洞等。代码静态检查能够在代码的规范性、安全性、可靠性、可维护性等方面起到重要做用。react
在客户端中,Android可使用CheckStyle、Lint、Findbugs、PMD等工具,iOS可使用Clang Static Analyzer、OCLint等工具。而在React Native的开发过程当中,针对于JavaScript的ESLint,与TypeScript的TSLint,则成为了主要代码静态检查的工具。本文将按照使用TSLint的缘由、使用TSLint的方法、自定义TSLint的步骤进行探究分析。git
在客户端团队进入React Native项目的开发过程当中,面临着以下问题:github
虽然以上问题能够经过屡次不断将雷点标记出,并不断地分享经验与强化代码Review过程等方式来进行缓解,可是仍面临着React Native开发者掌握的技术水平千差万别,知识分享传播的速度缓慢等问题,既致使了开发成本的不断增长和开发效率持续低下的问题,还难以免一个坑被踩了屡次的状况出现。这时急需一款能够知足如下目标的工具:typescript
根据上述要求的描述,静态检查工具TSLint能够较为有效地达成目标。npm
TSLint是硅谷企业Palantir的一个项目,它是一款能够检查TypeScript代码可读性、可维护性以及功能性错误的静态检查工具,当前许多编辑器(Editors)和构建系统(Build Systems)支持这一工具,同时支持自定义编写Lint规则、配置、格式化等。json
当前TSLint已经包含了上百条规则,这些规则构筑了当前TSLint检查的基础。在代码开发阶段中,经过这些配置好的规则能够给工程一个完整的检查,并随时能够提示出可能存在的问题。本文内容参考了TSLint官方文档https://palantir.github.io/tslint/。
如下规则主要来源于TSLint规则,是某些规则的简单介绍。
上述2.1所列出的规则来源于Palantir官方TSLint规则。实际还有多种,可能会用到的有如下:
咱们在项目的规则配置过程当中,通常采用上述规则包其中一种或者若干种同时配置,那如何配置呢?请看下文。
首先,在工程package.json文件中配置TSLint包:
在根目录中的tslint.json文件中能够根据须要配置已有规则,例如:
其中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/。
在完成工程配置后,须要下载所须要依赖包,要在工程所在根目录使用npm install
命令完成下载依赖包。
在完成下载依赖包后,IDE环境能够根据对应配置文件进行提示,能够实时地提示出存在问题代码的错误信息,以VSCode为例:
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
本地进行代码检查的过程也会存在被人遗忘的可能性,经过技术的保障,能够避免人为遗忘,做为代码提交的标准流程,经过CI检查后再合并代码,能够有效避免代码错误的问题。CI系统能够为理解为一个云端的环境,环境配置与本地一致,在这种状况下,能够生成与本地一致的报告,在美团内部可使用基于Jenkins的Castle CI系统, 生成结果与本地结果一致:
代码检查不止局限上述阶段,在代码commit、pull request、打包等阶段都可触发。
当前的TSLint规则虽然涵盖了比较广泛问题的一些代码检查,可是实践中仍是存在一些问题的:
基于以上缘由其余团队也有自定义TSLint的先例,例如上文提到的tslint-microsoft-contrib、tslint-eslint-rules等。
那自定义TSLint大概须要什么步骤呢,首先规则文件根据规范进行循序渐进的编写规则信息,而后根据代码检查逻辑对语法树进行分析并编写逻辑代码,这也是自定义规则的核心部分了,最后就是自定义规则的使用了。
自定义规则的示例直接参考官方的规则是最直接的,咱们能这里参考一个比较简单的规则"class-name"。
"class-name"规则上文已经提到,它的意思是对类命名进行规范,当团队中类相关的命名不规范,会致使项目代码风格不统一甚至其余出现的问题,而"class-name"规则能够有效解决这个问题。咱们能够看下具体的源码文件:https://github.com/palantir/tslint/blob/master/src/rules/classNameRule.ts。
而后将分步对此自定义规则进行讲解。
规则命名必须是符合如下2个规则:
规则的类名是Rule
,而且要继承Lint.Rules.AbstractRule
这个类型,固然也可能有继承TypedRule
这个类的时候,可是咱们经过阅读源码发现,其实它也是继承自Lint.Rules.AbstractRule
这个类。
metadata包含了配置参数,定义了规则的信息以及配置规则的定义。
规则类型有四种,分别为:"functionality"、"maintainability"、"style"、"typescript"。
这个主要是在检查出问题的时候进行提示的文字,并不局限于使用一个静态变量的形式,可是大部分官方规则都是这么编写,这里对此进行介绍,防止引发歧义。
apply
主要是进行静态检查的核心方法,经过返回applyWithFunction
方法或者返回applyWithWalker
来进行代码检查,其实applyWithFunction
方法与applyWithWalker
方法的主要区别在于applyWithWalker
能够经过IWalker
实现一个自定义的IWaker
类,区别以下:
其中实现IWaker
的抽象类AbstractWalker
里面也继承了WalkContext
,
而这个WalkContext
就是上面提到的applyWithFunction
的内部实现类。
不管是applyWithFunction
方法仍是applyWithWalker
方法中的IWaker
实现都传入了sourceFile
这个参数,这个至关于文件的根节点,而后经过ts.forEachChild
方法遍历整个语法树节点。
这里有两个查看AST语法树的工具:
AST Explorer
优势:
在AST Explorer能够高亮显示所选中代码对应的AST语法树信息。
缺点:
TypeScript AST Viewer
优势:
每一个版本对应对kind信息数值可能会变更,可是对应的枚举名字是固定的,以下图:
从而这个工具能够避免频繁根据其数值查找对应信息。
缺点: 不能高亮显示代码对应的AST语法树区域,定位效率较低。
综上,经过同时使用上述两个工具定位分析,能够有效地提升分析效率。
经过ts.forEachChild
方法对于语法树全部的节点进行遍历,在遍历的方法里能够实现本身的逻辑,其中节点的类为ts.Node
:
其中kind为当前节点的类型,固然Node
是全部节点的基类,它的实现还包括Statement
、Expression
、Declaration
等,回到开头这个"class-name"规则,咱们的全部声明类主要是class与interface关键字,分别对应ClassExpression
、ClassDeclaration
、InterfaceDeclaration
, 咱们能够经过上步提到的AST语法树工具,在语法树中看到其为一一对应的。
在规则代码中主要经过isClassLikeDeclaration
、isInterfaceDeclaration
这两个方法进行判断的。
其中isClassLikeDeclaration
、isInterfaceDeclaration
对应的方法咱们能够在node.js文件中找到:
判断是对应的类型时,调用addFailureAtNode
方法把错误信息和节点传入,固然还能够调用addFailureAt
、addFailure
方法。
最终这个规则编写结束了,有一点再次强调下,由于每一个版本所对应的类型代码可能不相同,当判断kind的时候,必定不要直接使用各个类型对应的数字。
完成规则代码后,是ts后缀的文件,而ts规则文件实际仍是要用js文件,这时候咱们须要用命令将ts转化为js文件:
tsc ./src/*.ts --outDir dist
将ts规则生成到dist文件夹(这个文件夹命名用户自定),而后在tslint.json文件中配置生成的规则文件便可。
以后在项目的根目录里面,使用如下命令既可进行检查:
tslint --project tsconfig.json --config tslint.json
同时为了将来新增规则以及规则配置的更好的操做性,建议能够封装到本身的规则包,以便与规则的管理与传播。
在美团,有十余个页面的单个工程首次接入TSLint后,检查出的问题有近百条。可是因为开启的规则不一样,配置规则包的差别,检查后的数量可能为几十条到几千条甚至更多。如今已开发十余条自定义规则,在单个工程内,处理优化了数百处可能存在问题的代码。最终TSLint接入了相关React Native开发团队,成为了代码提交阶段的必要步骤。
经过团队内部的验证,文章开头遇到的问题获得了有效地缓解,目标基本达到预期。TSLint在React Native开发过程当中既保证了代码风格的统一,又保证了React Native开发人员的开发质量,避免了许多低级错误,有效地节省了问题排查和人员沟通的成本。
同时利用自定义规则,可以将一些兼容性问题在内的个性化问题进行总结与预防,提升了开发效率,不用花费大量时间查找问题代码,又避免了在一个问题上跌倒屡次的状况出现。对于不一样经验的开发者而言,不只能够进行友好的提示,也能够帮助快速地定位问题,将一我的遇到的经验教训,用极低的成本扩散到其余团队之中,将开发状态从“亡羊补牢”进化到“防患未然”。
家正,美团点评Android高级工程师。2017 年加入美团点评,负责美团大交通的业务开发。