使用 ESLint 检查 TypeScript 代码

BenjaminHar 6年前
   <h2>TypeScript 与 ESLint</h2>    <p><a href="/misc/goto?guid=4959755565666003911" rel="nofollow,noindex">ESLint</a> 是一个代码检查工具,主要用来发现代码错误、统一代码风格,目前已被广泛的应用于各种 JavaScript 项目中。</p>    <p>它通过插件化的特性极大的丰富了适用范围,搭配 <a href="/misc/goto?guid=4959755565770185879" rel="nofollow,noindex">typescript-eslint-parser </a>之后,甚至可以用来检查 TypeScript 代码。</p>    <h2>为什么需要 ESLint</h2>    <p>TypeScript 除了是一个编译 ts 文件的工具以外,还可以 <a href="/misc/goto?guid=4959755565869277129" rel="nofollow,noindex">作为一个静态代码检查工具使用</a> 。</p>    <p>TypeScript 会对文件进行语法解析,如果遇到一些语法错误,或使用了未定义的变量,则会报错。</p>    <p>ESLint 也会对文件进行语法解析,它可以对一些代码风格进行约束,发现未定义的变量,但是对于错误的属性或方法引用,却无能为力。</p>    <p>我们对同样一段代码分别运行 tsc 和 eslint ,会得到如下报错信息:</p>    <pre>  let myName = 'Tom';    console.log(`My name is ${myNane}`);  console.log(`My name is ${myName.toStrng()}`);  console.log(`My name is ${myName}`)    // tsc 报错信息:  //  // index.ts(3,27): error TS2552: Cannot find name 'myNane'. Did you mean 'myName'?  // index.ts(4,34): error TS2551: Property 'toStrng' does not exist on type 'string'. Did you mean 'toString'?  //  //  // eslint 报错信息:  //  // /path/to/index.ts  //   3:27  error  'myNane' is not defined         no-undef  //   5:38  error  Missing semicolon               semi  //  // ✖ 2 problems (2 errors, 0 warnings)  //   1 errors, 0 warnings potentially fixable with the `--fix` option.</pre>    <table>     <thead>      <tr>       <th>存在的问题</th>       <th>tsc 是否报错</th>       <th>eslint 是否报错</th>      </tr>     </thead>     <tbody>      <tr>       <td>myName 被勿写成了 myNane</td>       <td>:white_check_mark:</td>       <td>:white_check_mark:</td>      </tr>      <tr>       <td>toString 被勿写成了 toStrng</td>       <td>:white_check_mark:️</td>       <td>:x:</td>      </tr>      <tr>       <td>少了一个分号</td>       <td>:x:</td>       <td>:white_check_mark:</td>      </tr>     </tbody>    </table>    <p>上例中,由于 eslint 无法识别 myName 存在哪些方法,所以对于拼写错误的 toString 没有检查出来。而代码风格的错误不影响编译,故 tsc 没有检查出来。</p>    <p>未定义的变量两者都能检查出来。</p>    <p>值得注意的是, tsc 不仅检查出来了代码问题,还非常智能的给出了修改建议。</p>    <p>下面是 TypeScirpt 作为一个静态代码检查工具,与 ESLint 的关系图:</p>    <p style="text-align: center"><img src="https://simg.open-open.com/show/2877454c9786b89636599dddaaa5000d.png"></p>    <p>上图中,TypeScript 与 ESLint 有重叠的部分,也有各自独立的部分,虽然发现代码错误比统一的代码风格更重要,但是当一个项目越来越庞大,开发人员也越来越多的时候,代码风格的约束还是必不可少的。</p>    <p>下面我们就来一步一步给 TypeScript 项目添加 ESLint 检查。</p>    <h2>安装</h2>    <p>ESLint 可以安装在当前项目中或全局环境下,因为代码检查是项目的重要组成部分,所以我们一般会将它安装在当前项目中。可以运行下面的脚本来安装:</p>    <pre>  npm install eslint --save-dev</pre>    <p>由于 ESLint 默认使用 <a href="/misc/goto?guid=4959755565956738094" rel="nofollow,noindex">Espree</a> 进行语法解析,无法识别 TypeScript 的一些语法,故我们需要安装 typescript-eslint-parser ,替代掉默认的解析器,别忘了同时安装 typescript :</p>    <pre>  npm install typescript typescript-eslint-parser --save-dev</pre>    <p>由于 typescript-eslint-parser 对一部分 ESLint 规则支持性不好,故我们需要安装 eslint-plugin-typescript ,弥补一些支持性不好的规则。</p>    <pre>  npm install eslint-plugin-typescript --save-dev</pre>    <h2>创建配置文件</h2>    <p>ESLint 需要一个配置文件来决定对哪些规则进行检查,配置文件的名称一般是 .eslintrc.js 或 .eslintrc.json 。</p>    <p>当运行 ESLint 的时候检查一个文件的时候,它会首先尝试读取该文件的目录下的配置文件,然后再一级一级往上查找,将所找到的配置合并起来,作为当前被检查文件的配置。</p>    <p>我们在项目的根目录下创建一个 .eslintrc.js ,内容如下:</p>    <pre>  module.exports = {      parser: 'typescript-eslint-parser',      plugins: [          'typescript'      ],      rules: {          // @fixable 必须使用 === 或 !==,禁止使用 == 或 !=,与 null 比较时除外          'eqeqeq': [              'error',              'always',              {                  null: 'ignore'              }          ],          // 类和接口的命名必须遵守帕斯卡命名法,比如 PersianCat          'typescript/class-name-casing': 'error'      }  }</pre>    <p>以上配置中,我们指定了两个规则,其中 eqeqeq 是 ESLint 原生的规则(它要求必须使用 === 或 !== ,禁止使用 == 或 != ,与 null 比较时除外), typescript/class-name-casing 是 eslint-plugin-typescript 为 ESLint 增加的规则(它要求类和接口的命名必须遵守帕斯卡命名法,比如 PersianCat )。</p>    <p>规则的取值一般是一个数组(上例中的 eqeqeq ),其中第一项是 off 、 warn 或 error 中的一个,表示关闭、警告和报错。后面的项都是该规则的其他配置。</p>    <p>如果没有其他配置的话,则可以将规则的取值简写为数组中的第一项(上例中的 typescirpt/class-name-casing )。</p>    <p>关闭、警告和报错的含义如下:</p>    <ul>     <li>关闭:禁用此规则</li>     <li>警告:代码检查时输出错误信息,但是不会影响到 exit code</li>     <li>报错:发现错误时,不仅会输出错误信息,而且 exit code 将被设为 1(一般 exit code 不为 0 则表示执行出现错误)</li>    </ul>    <h2>检查一个 ts 文件</h2>    <p>创建了配置文件之后,我们来创建一个 ts 文件看看是否能用 ESLint 去检查它了。</p>    <p>创建一个新文件 index.ts ,将以下内容复制进去:</p>    <pre>  interface person {      name: string;      age: number;  }    let tom: person = {      name: 'Tom',      age: 25  };    if (tom.age == 25) {      console.log(tom.name + 'is 25 years old.');  }</pre>    <p>然后执行以下命令:</p>    <pre>  ./node_modules/.bin/eslint index.ts</pre>    <p>则会得到如下报错信息:</p>    <pre>  /path/to/index.ts     1:11  error  Interface 'person' must be PascalCased  typescript/class-name-casing    11:13  error  Expected '===' and instead saw '=='     eqeqeq    ✖ 2 problems (2 errors, 0 warnings)</pre>    <p>上面的结果显示,刚刚配置的两个规则都生效了:接口 person 必须写成帕斯卡命名规范, == 必须写成 === 。</p>    <p>需要注意的是,我们使用的是 ./node_modules/.bin/eslint ,而不是全局的 eslint 脚本,这是因为代码检查是项目的重要组成部分,所以我们一般会将它安装在当前项目中。</p>    <p>可是每次执行这么长一段脚本颇有不便,我们可以在 package.json 中添加一个 script 来简化这个步骤:</p>    <pre>  {      "scripts": {          "eslint": "eslint index.ts"      }  }</pre>    <p>这时只需执行 npm run eslint 即可。</p>    <h2>检查整个项目的 ts 文件</h2>    <p>我们的项目源文件一般是放在 src 目录下,所以需要将 package.json 中的 eslint 脚本改为对一个目录进行检查。由于 eslint 默认不会检查 .ts 后缀的文件,所以需要加上参数 --ext .ts :</p>    <pre>  {      "scripts": {          "eslint": "eslint src --ext .ts"      }  }</pre>    <p>此时执行 npm run eslint 即会检查 src 目录下的所有 .ts 后缀的文件。</p>    <h2>在 VSCode 中集成 ESLint 检查</h2>    <p>在编辑器中集成 ESLint 检查,可以在开发过程中就发现错误,极大的增加了开发效率。</p>    <p>要在 VSCode 中集成 ESLint 检查,我们需要先安装 ESLint 插件,点击「扩展」按钮,搜索 ESLint,然后安装即可。</p>    <p>VSCode 中的 ESLint 插件默认是不会检查 .ts 后缀的,需要在「文件 => 首选项 => 设置」中,添加以下配置:</p>    <pre>  {      "eslint.validate": [          "javascript",          "javascriptreact",          "typescript"      ]  }</pre>    <p>这时再打开一个 .ts 文件,将鼠标移到红色提示处,即可看到这样的报错信息了:</p>    <p style="text-align: center"><img src="https://simg.open-open.com/show/8fb96828e6fb7b0831bcc0c24c956271.png"></p>    <h2>使用已完善的配置</h2>    <p>ESLint 原生的规则和 eslint-plugin-typescript 的规则太多了,而且原生的规则有一些在 TypeScript 中支持的不好,需要禁用掉。</p>    <p>这里我推荐使用 <a href="/misc/goto?guid=4959755566054444453" rel="nofollow,noindex">AlloyTeam ESLint 规则中的 TypeScript 版本</a> ,它已经为我们提供了一套完善的配置规则。</p>    <p>安装:</p>    <pre>  npm install --save-dev eslint typescript typescript-eslint-parser eslint-plugin-typescript eslint-config-alloy</pre>    <p>在你的项目根目录下创建 .eslintrc.js ,并将以下内容复制到文件中:</p>    <pre>  module.exports = {      extends: [          'eslint-config-alloy/typescript',      ],      globals: {          // 这里填入你的项目需要的全局变量          // 这里值为 false 表示这个全局变量不允许被重新赋值,比如:          //          // jQuery: false,          // $: false      },      rules: {          // 这里填入你的项目需要的个性化配置,比如:          //          // // @fixable 一个缩进必须用两个空格替代          // 'indent': [          //     'error',          //     2,          //     {          //         SwitchCase: 1,          //         flatTernaryExpressions: true          //     }          // ]      }  };</pre>    <h2>使用 ESLint 检查 tsx 文件</h2>    <p>如果需要同时支持对 tsx 文件的检查,则需要对以上步骤做一些调整:</p>    <h3>安装 eslint-plugin-react</h3>    <pre>  npm install --save-dev eslint-plugin-react</pre>    <h3>package.json 中的 scripts.eslint 添加 .tsx 后缀</h3>    <pre>  {      "scripts": {          "eslint": "eslint src --ext .ts,.tsx"      }  }</pre>    <h3>VSCode 的配置中新增 typescriptreact 检查</h3>    <pre>  {      "eslint.validate": [          "javascript",          "javascriptreact",          "typescript",          "typescriptreact"      ]  }</pre>    <h3>使用 AlloyTeam ESLint 规则中的 TypeScript React 版本</h3>    <p><a href="/misc/goto?guid=4959755566139432366" rel="nofollow,noindex">AlloyTeam ESLint 规则中的 TypeScript React 版本</a></p>    <h2>Troubleshootings</h2>    <h3>Cannot find module 'typescript-eslint-parser'</h3>    <p>你运行的是全局的 eslint,需要改为运行 ./node_modules/.bin/eslint 。</p>    <h3>cannot read property type of null</h3>    <p>需要关闭 eslint-plugin-react 中的规则 react/jsx-indent 。</p>    <h3>VSCode 没有显示出 ESLint 的报错</h3>    <ol>     <li>检查「文件 => 首选项 => 设置」中有没有配置正确</li>     <li>检查必要的 npm 包有没有安装</li>     <li>检查 .eslintrc.js 有没有配置</li>     <li>检查文件是不是在 .eslintignore 中</li>    </ol>    <p>如果以上步骤都不奏效,则可以在「文件 => 首选项 => 设置」中配置 "eslint.trace.server": "messages" ,按 Ctrl + Shift + U 打开输出面板,然后选择 ESLint 输出,查看具体错误。</p>    <p style="text-align: center"><img src="https://simg.open-open.com/show/73c10a271b0e1953c10685fe06fdfc66.png"></p>    <p> </p>