TypeScript lint 的性能瓶颈,往往不在语法扫描,而在类型信息构建。只看 AST 的规则可以很快,但一旦规则需要判断 Promise 是否被处理、类型断言是否安全,或两个表达式在语义上是否兼容,就必须借助 TypeScript 编译器的语义分析。
tsgolint v7 已进入稳定阶段,它通过 typescript-go 编译器提供类型语义,并把这部分能力接入 Oxlint。Oxlint 继续负责配置解析和文件发现,tsgolint 则执行需要类型信息的规则。该版本兼容 TypeScript 7.0.2,并覆盖 61 条类型感知规则中的 59 条,目标是在保留语义检查能力的同时,显著降低相较 ESLint 类型检查链路的运行成本。
快速扫描和类型检查,终于可以明确分工
传统 TypeScript lint 工具通常在一个进程模型里完成文件发现、规则调度、AST 遍历和类型查询。项目规模增大后,构建类型图和反复访问编译器 API 会成为主要开销。
tsgolint 与 Oxlint 的组合采用了更清晰的职责划分:
- Oxlint 读取配置、发现待检查文件,并承担常规 lint 工作。
- tsgolint 处理必须依赖类型信息的规则。
- typescript-go 提供 TypeScript 语义分析能力,让类型感知检查能够利用 Go 原生实现的性能特征。
这并不意味着每条 lint 规则都会突然变快。格式、命名、语法模式等规则本来就不需要类型系统;真正受益的是必须先理解程序语义才能判断的问题。把这类规则从常规扫描中区分出来,也方便团队在本地开发、提交检查和 CI 中采用不同策略。
59 条规则已经可用,但迁移不能只看数字
v7 实现了 61 条类型感知规则中的 59 条,覆盖率已经足以支撑大多数实际项目。不过,“支持 59 条”不等于可以把现有 ESLint 配置原样复制过来。
迁移时应逐项检查:
- 现有配置究竟启用了哪些类型感知规则。
- 剩余两条尚未覆盖的规则是否属于项目的强制质量门槛。
- 同名规则在边界输入、自动修复和报错位置上是否存在差异。
- monorepo 中的每个包能否找到正确的
tsconfig.json、项目引用和生成类型文件。 - 编辑器、提交钩子与 CI 是否使用了相同版本和相同入口。
稳定版意味着它更适合进入正式工程评估,但不代表迁移可以跳过基线对比。尤其是大量使用 project references、路径别名或生成代码的仓库,应先选一个包做影子运行。
可以这样搭建一个最小验证项目
下面的示例假设当前 Oxlint CLI 提供 --type-aware 入口,并使用 oxlint-tsgolint v7。运行前可执行 npx oxlint --help,确认安装版本的参数名称;如果项目锁定了不同版本,应以对应版本文档为准。
mkdir tsgolint-v7-demo
cd tsgolint-v7-demo
npm init -y
npm install --save-dev typescript oxlint oxlint-tsgolint@^7
cat > tsconfig.json <<'JSON'
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"noEmit": true
},
"include": ["src/**/*.ts"]
}
JSON
mkdir src
cat > src/index.ts <<'TS'
async function persistUser(name: string): Promise<void> {
if (name.length === 0) {
throw new Error("name is required");
}
}
// 当项目启用相应的 Promise 类型感知规则时,这类调用应被检查。
persistUser("Ada");
TS
npx oxlint --type-aware .
这个项目的重点不是保证默认配置一定报告某条特定规则,而是验证三件事:CLI 能否启动类型感知模式、工具能否读取 tsconfig.json,以及 TypeScript 文件是否进入正确的语义分析链路。具体诊断结果取决于当前版本和项目启用的规则集合。
确认命令可用后,可以在 package.json 中把快速检查和类型检查拆开:
{
"scripts": {
"lint": "oxlint .",
"lint:types": "oxlint --type-aware .",
"lint:ci": "npm run lint && npm run lint:types"
}
}
这种划分适合渐进式接入:开发者保存文件时运行快速 lint,提交前或 CI 再执行完整的类型感知规则。如果仓库规模不大,也可以直接把 lint:ci 作为统一入口。
性能评估要测自己的仓库
发布信息表明,tsgolint 相较 ESLint 展现出显著性能提升,但团队不应把别人的基准数字直接写进容量规划。lint 耗时还会受到文件数量、类型复杂度、磁盘缓存、monorepo 边界和 CI 机器规格影响。
可以用相同提交做一个简单的重复测量:
# 先预热依赖和文件缓存,再分别重复运行。
time npm run lint:types
time npm run lint:types
# 保留旧 ESLint 脚本时,用同一工作区和同一台机器比较。
time npm run lint:eslint
time npm run lint:eslint
除了总耗时,还应记录峰值内存、冷启动时间、增量修改后的耗时,以及诊断数量是否一致。速度更快但漏掉团队依赖的规则,并不是一次成功迁移。
建议采用双轨验证,而不是一次性替换
较稳妥的落地方式是让旧链路和新链路并行运行一段时间:
- 固定 Oxlint、tsgolint 和 TypeScript 相关依赖版本,避免 CI 漂移。
- 导出现有 ESLint 类型感知规则清单,与 59 条已支持规则逐项核对。
- 先让 tsgolint 只生成报告,不立即阻塞合并。
- 对比误报、漏报、诊断位置和执行时间。
- 确认关键规则等价后,再把新链路提升为必过检查。
- 暂时保留尚未覆盖的规则,必要时让 ESLint 只执行这部分规则。
v7 的意义不只是“用 Go 重写后更快”,而是让 Oxlint 从快速语法检查进一步进入 TypeScript 语义检查领域。对于被类型感知 lint 拖慢的中大型代码库,它值得尽快验证;但真正可靠的采用标准仍然是规则覆盖、诊断一致性和本仓库中的实际性能,而不是单独一个基准数字。