ESLint 批量抑制:技术债治理的正确打开方式

0 阅读3分钟

ESLint 批量抑制:技术债治理的正确打开方式

引子:一个技术 Leader 的困境

作为技术 Leader,你可能遇到过这样的场景:

团队维护着一个两年前的 React 项目,20 万行代码,技术债堆积如山。你想推动代码质量提升,在团队会议上提议:"我们开启 @typescript-eslint/no-explicit-any 规则吧,减少 any 的滥用。"

然后你在本地试运行了一下:

$ eslint .
✖ 1,247 problems (1,247 errors, 0 warnings)

1,247 个错误

你的第一反应可能是:"算了,还是别开了。" 团队成员也松了一口气——毕竟谁也不想在 JIRA 里看到一个"修复 1,247 个 Lint 错误"的任务。

但问题是,如果现在不开启规则,新代码还会继续使用 any,技术债只会越滚越大。这就是典型的"存量问题拖累增量改进"的困境。

今天要介绍的 ESLint 批量抑制(Bulk Suppressions) 功能,就是为了解决这个问题而生的。

核心思路:既往不咎,严控增量

批量抑制的核心理念可以用八个字概括:既往不咎,严控增量

工作机制

当你启用批量抑制后,ESLint 会做三件事:

  1. 记录现状:将当前所有违规记录到 eslint-suppressions.json 文件中
  2. 暂时放行:对于已记录的旧代码违规,ESLint 暂时忽略,CI/CD 正常通过
  3. 严格卡控:对于新增的代码,规则立即生效,任何新违规都会报错

看一个实际的抑制文件示例:

{
  "suppressions": [
    {
      "ruleId": "@typescript-eslint/no-explicit-any",
      "file": "src/components/UserProfile.tsx",
      "count": 12
    },
    {
      "ruleId": "@typescript-eslint/no-explicit-any",
      "file": "src/utils/request.ts",
      "count": 8
    }
  ]
}

这个文件告诉 ESLint:"UserProfile.tsx 文件里有 12 个 any,这是历史遗留问题,暂时放过。但如果出现第 13 个,立即报错。"

监控机制

批量抑制不是"一抑了之",而是有严格的监控:

// src/components/UserProfile.tsx
// 已记录 12 个 any,当前 12 个 →  通过

// 如果开发者新增了一个 any:
function handleData(data: any) {  // 这是第 13 个!
  // ...
}

// ESLint 会立即报告该文件的所有违规:
// ✖ 13 problems in src/components/UserProfile.tsx

一旦违规数超标,ESLint 会暴露该文件的所有问题,倒逼开发者要么修复新问题,要么顺手把旧问题也一起解决。

实战:在 React + TypeScript 项目中落地

场景设定

假设你负责一个电商项目,技术栈是 React + TypeScript,团队 8 人,代码库现状:

  • 150+ 个组件文件
  • TypeScript 配置较宽松(strict: false
  • 大量使用 any、非空断言、隐式类型转换
  • 团队对"突然冒出的几百个 Lint 错误"非常抵触

你的目标是:在不影响现有开发节奏的前提下,逐步提升代码质量

第一步:评估现状

先看看如果直接开启严格规则会怎样:

// eslint.config.js
export default [
  {
    files: ["**/*.{ts,tsx}"],
    rules: {
      "@typescript-eslint/no-explicit-any": "error",
      "@typescript-eslint/no-non-null-assertion": "error",
      "@typescript-eslint/strict-boolean-expressions": "error"
    }
  }
];

运行检查:

$ eslint .

src/components/ProductList.tsx
  12:15  error  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
  23:8   error  Forbidden non-null assertion              @typescript-eslint/no-non-null-assertion
  ...

src/utils/formatPrice.ts
  5:22   error  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
  ...

✖ 847 problems (847 errors, 0 warnings)

847 个错误。如果强行推进,结果可想而知:

  • 团队成员抱怨:"又要加班修 Bug 了"
  • CI/CD 构建失败,阻塞所有人的开发
  • 最后可能不得不回滚配置

第二步:使用批量抑制

冷静下来,换个思路——用批量抑制:

# 1. 开启规则并生成抑制文件
$ eslint . --suppress-all --fix

# 自动修复了 142 个问题
# 剩余 705 个问题记录到 eslint-suppressions.json

✔ Suppressions written to eslint-suppressions.json

这个命令做了两件事:

  1. 自动修复:能自动修复的问题(比如添加类型注解)直接修复
  2. 记录存量:无法自动修复的问题记录到抑制文件

现在再运行 eslint .

$ eslint .
✔ No problems found

完美通过!关键是,新代码必须符合规则

第三步:验证增量卡控

让我们验证一下,新代码是否真的会被拦截。

某个开发者在新功能中写了这样的代码:

// src/components/NewFeature.tsx (新文件)
import React from 'react';

interface Props {
  data: any;  // x 新代码使用 any
}

export default function NewFeature({ data }: Props) {
  return <div>{data.name}</div>;
}

运行检查:

$ eslint src/components/NewFeature.tsx

src/components/NewFeature.tsx
  4:9  error  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any

✖ 1 problem (1 error, 0 warnings)

立即报错!因为这是新文件,不在抑制列表中。

开发者必须修复才能提交:

// 修复后
interface ProductData {
  name: string;
  price: number;
}

interface Props {
  data: ProductData;  //  使用明确的类型
}

第四步:建立日常工作流

1. 定期清理已修复的抑制

当团队修复了一些旧代码后,使用 --prune-suppressions 清理:

$ eslint . --prune-suppressions

✔ Removed 23 suppressions that are no longer needed

这个命令会:

  • 检查每个抑制项是否还存在对应的违规
  • 如果违规已修复,从抑制文件中移除该记录

2. Code Review 关注点

在代码评审时,增加一个检查项:

## Code Review Checklist

- [ ] 代码逻辑正确
- [ ] 测试覆盖充分
- [ ] **技术债趋势**  - [ ] 没有新增 eslint-suppressions.json 中的抑制项
  - [ ] 如果修改了旧文件,是否顺手修复了部分 Lint 问题

进阶技巧

技巧 1: 按模块分批抑制

如果你想先对核心模块严格要求,可以这样操作:

# 先对 utils 和 hooks 目录开启严格模式
$ eslint src/utils src/hooks --suppress-all --fix

# 其他目录暂时不检查

技巧 2: 结合 Git Hooks

推荐使用 lint-staged 来精确控制只检查暂存区的文件,比手写脚本更稳健:

  1. 安装依赖:

    npm install --save-dev lint-staged husky
    
  2. 配置 package.json

    {
      "lint-staged": {
        "*.{ts,tsx}": [
          "eslint"
        ]
      }
    }
    
  3. .husky/pre-commit 中调用:

    #!/bin/sh
    npx lint-staged
    

这样,每次提交时,ESLint 只会检查你修改的文件。如果有新增的违规(且未被抑制),提交将被拦截。

技巧 3: 制定“童子军规则”

在团队中推行童子军规则(Boy Scout Rule):

"离开时,让代码比你来时更干净一点。"

具体做法:

  • 如果你修改了某个旧文件,顺手修复 1-2 个 Lint 问题
  • 在 Code Review 时,给予"技术债修复"额外的认可

总结:技术债治理的三个原则

通过 ESLint 批量抑制功能,我们学到的不仅是一个工具的使用,更是一种技术债治理的方法论

原则 1: 不要让存量问题阻碍增量改进

传统思维:"要么全修,要么不修"。 批量抑制思维:"存量暂时冻结,增量严格卡控"。

原则 2: 渐进式改进优于一次性重构

一次性重构的风险:

  • 周期长,风险高
  • 影响业务交付
  • 团队抵触情绪大

渐进式改进的优势:

  • 每次改进可见、可控
  • 不影响正常迭代
  • 团队负担小

原则 3: 用工具和流程保障,而非靠自觉

不要指望团队成员"自觉地"提升代码质量。应该:

  • 工具拦截:ESLint 自动检查新代码
  • CI 监控:自动检测技术债趋势
  • 流程保障:Code Review 检查清单

参考资料


如果这篇文章对你有帮助,欢迎点赞收藏!也欢迎在评论区分享你在技术债治理方面的经验和困惑。