第四篇: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-push 或 CI:运行全量
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。