第四篇:ESLint 与类型规则的 CI 卡点 —— 在 Git Hook 阶段强行拦截劣质类型

第四篇:ESLint 与类型规则的 CI 卡点 —— 在 Git Hook 阶段强行拦截劣质类型

为何要在 CI 中强制类型检查?

TypeScript 的类型检查虽然能在编辑器中提示错误,但开发者可能忽略或使用 // @ts-ignore 跳过。为了保证代码库的长期健康,必须在提交前(pre-commit)和 PR 合并前(CI)强制执行完整的类型检查,并结合 ESLint 规则约束编码风格。

配置 ESLint 使用 TypeScript 解析器

首先安装依赖:

bash
npm install -D @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint

然后在 .eslintrc.js 中配置:

javascript
module.exports = { parser: '@typescript-eslint/parser', plugins: ['@typescript-eslint'], extends: [ 'eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:@typescript-eslint/recommended-requiring-type-checking', // 启用类型规则 ], parserOptions: { project: './tsconfig.json', tsconfigRootDir: __dirname, }, rules: { // 禁止使用 any '@typescript-eslint/no-explicit-any': 'error', // 禁止不必要的类型断言 '@typescript-eslint/no-unnecessary-type-assertion': 'warn', // 要求显式函数返回类型 '@typescript-eslint/explicit-function-return-type': ['warn', { allowExpressions: true }], // 强制使用 strict 规则族 '@typescript-eslint/strict-boolean-expressions': 'error', '@typescript-eslint/no-floating-promises': 'error', '@typescript-eslint/no-misused-promises': 'error', } };

添加 tsc --noEmit 到检查流程

ESLint 的类型规则依赖类型信息,但无法捕获所有 TypeScript 编译错误(如属性不存在、类型不匹配)。因此必须单独运行 tsc --noEmit

package.json 中添加脚本:

json
{ "scripts": { "type-check": "tsc --noEmit", "lint": "eslint . --ext .ts,.tsx", "validate": "npm run lint && npm run type-check" } }

集成到 Git Hook(使用 husky + lint-staged)

以下示例基于 husky v8。husky v9 的 API 有较大变化,请根据实际版本调整。

安装 husky 和 lint-staged:

bash
npm install -D husky lint-staged npx husky install

package.json 中配置 lint-staged:

json
{ "lint-staged": { "*.{ts,tsx}": [ "eslint --fix" ] } }

重要tsc --noEmit 不能放在 lint-staged 中。因为 tsc 不接受文件列表作为参数,而 lint-staged 会传递暂存文件路径给它,导致行为异常。tsc 需要完整的项目上下文(通过 tsconfig.json),必须全量运行。

配置 pre-commit hook:

bash
# .husky/pre-commit (husky v8) #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" npx lint-staged

推荐策略

  • pre-commit:只运行 lint-staged(ESLint 修复),保证快速
  • pre-pushCI:运行全量 npm run validate(ESLint + type-check)

如果团队要求提交前必须类型检查,可以在 pre-commit 中运行全量检查(适合中小项目):

bash
# .husky/pre-commit #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" npm run validate # 包含 lint 和 type-check

对于大型项目,全量 tsc 较慢,可以考虑:

  • 使用 fork-ts-checker-webpack-plugin 在开发时并行检查
  • 在 CI 中做全量检查,本地 pre-commit 只做 ESLint

在 CI(GitHub Actions)中强制执行

yaml
name: CI on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: 18 - run: npm ci - run: npm run lint - run: npm run type-check - run: npm run build

处理 // @ts-ignore// @ts-expect-error

ESLint 规则 @typescript-eslint/ban-ts-comment 可以禁止或限制这些注释:

javascript
rules: { '@typescript-eslint/ban-ts-comment': [ 'error', { 'ts-expect-error': 'allow-with-description', 'ts-ignore': false, // 禁止使用 ts-ignore 'ts-nocheck': true, 'ts-check': false, } ] }

这样开发者必须使用 ts-expect-error(预期会失败的类型检查)并添加描述说明原因,强制合理性。ts-ignore 则完全禁止,避免滥用。

实战:结合 pre-push 增加增量检查

有时 pre-commit 全量检查太慢,可以配置在 pre-push 阶段运行全量检查,而 pre-commit 只做 eslint fix:

bash
# .husky/pre-push #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" npm run validate
bash
# .husky/pre-commit #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" npx lint-staged

总结与思考

  • 类型检查必须成为自动化的一部分,不能依赖人工。
  • 利用 @typescript-eslint 的 strict 规则,可以大幅减少潜在运行时错误。
  • tsc --noEmit 和 lint-staged 的职责分离:前者需要项目全量,后者针对暂存文件。
  • 结合 Git Hook 和 CI 双重保障,确保每个 PR 都是"类型干净"的。
  • 思考:如果项目中已经存在大量 any,如何渐进式地启用 no-explicit-any 规则而不阻塞开发?提示:使用 ESLint 的 overrides 按目录渐进启用,或先设为 warn,逐步修复后改为 error
返回知识中心