typescript-eslint 的 ESLint 插件@typescript-eslint/eslint-plugin:规则系统与共享配置完全指南
【免费下载链接】typescript-eslint:sparkles: Monorepo for all the tooling which enables ESLint to support TypeScript项目地址: https://gitcode.com/GitHub_Trending/ty/typescript-eslint
@typescript-eslint/eslint-plugin是 typescript-eslint 项目为 ESLint 提供的 TypeScript 规则插件,负责把数百条针对 TypeScript 语法与类型系统的 lint 规则加载进 ESLint,并通过预置的共享配置一键启用。本文将围绕该插件的导出结构、内置配置矩阵、类型感知(typed linting)规则的开启方式与规则实现原理展开,结合当前仓库源码给出可直接复制的配置示例,帮助你在 Flat Config 与 Legacy Config 两种模式下正确安装、配置与使用它。
插件定位:ESLint 与 TypeScript 之间的规则层
@typescript-eslint/eslint-plugin是一个标准的 ESLint 插件包,用于向 ESLint 加载 typescript-eslint 提供的自定义规则(custom rules)与规则配置列表(rule configurations lists)。其核心文档(docs/packages/ESLint_Plugin.mdx)给出了两个关键事实:
- 这些规则依赖
@typescript-eslint/parser将 TypeScript 代码解析成 ESLint 兼容的 AST 节点; - 对于类型感知规则,parser 还要负责提供底层的 TypeScript Program(类型检查程序),规则才能访问类型信息。
也就是说,插件本身不解析代码,解析交给 parser;插件只负责“有哪些规则、规则如何配置、按什么配置集合启用”,两者组合才构成完整的 TypeScript linting 链路。在 Flat Config(eslint.config.*)下,如果直接使用typescript-eslint聚合包(即tseslinthelper),甚至不需要单独安装本插件与 parser——聚合包已经把插件、parser 和内置配置装配好。
安装与版本要求
当前仓库中该包的元数据(packages/eslint-plugin/package.json)明确定义了运行时约束:
- 包名:
@typescript-eslint/eslint-plugin,当前版本 8.70.0; - Node 版本:
^18.18.0 || ^20.9.0 || >=21.1.0; - peerDependencies:
@typescript-eslint/parser(workspace:^,即与插件同版本的 parser);eslint:^8.57.0 || ^9.0.0 || ^10.0.0(同时兼容 ESLint 8/9/10);typescript:>=4.8.4 <6.1.0。
插件内部还声明了对同仓库多个工作区包的依赖,包括@typescript-eslint/scope-manager、@typescript-eslint/type-utils、@typescript-eslint/utils、@typescript-eslint/visitor-keys,以及@eslint-community/regexpp、ignore、natural-compare、ts-api-utils等工具库。
包的导出结构(exports字段)包含三部分:
| 导出入口 | 说明 |
|---|---|
.(默认入口) | index.d.ts/dist/index.js,即常规的插件对象(含configs、rules、meta) |
./use-at-your-own-risk/rules | rules.d.ts/dist/rules/index.js,以use-at-your-own-risk前缀暴露全部规则模块,供内部或高级用法按需引用 |
./use-at-your-own-risk/raw-plugin | raw-plugin.d.ts/dist/raw-plugin.js,暴露“原始”插件与 flat 配置工厂,前缀同样提示该 API 不受稳定性承诺保护 |
注意use-at-your-own-risk前缀是刻意的:这些入口不参与语义化版本的稳定性承诺,普通项目应使用默认入口。
插件导出的两个核心对象:configs与rules
插件的正式文档(docs/packages/ESLint_Plugin.mdx)将导出内容归纳为两张表:
| 名称 | 描述 |
|---|---|
configs | 字符串名称到可扩展 ESLint 配置设置(extendable config)的映射 |
rules | 字符串名称到规则对象(rule objects)的映射 |
这一设计在源码中得到完整印证。插件入口 packages/eslint-plugin/src/index.ts 的逻辑分为两步:
- 版本守卫:读取
typescript.versionMajorMinor,若主版本 >= 7 则打印错误说明并直接抛出异常——当前该插件不支持 TS 7.0,需要按提示使用 TS 6 API 的过渡方案; - 导出插件:
export = rawPlugin.plugin,把真正组装好的插件对象暴露给 ESLint。
插件对象的组装发生在 packages/eslint-plugin/src/raw-plugin.ts,其结构为:
const plugin = { configs: { /* 13 个传统命名配置 + flat 命名配置 */ }, meta: { name, namespace: '@typescript-eslint', version }, rules, // 来自 ./rules/index.ts 的规则映射 };其中configs同时挂载了两套命名:传统名称(如recommended、strict-type-checked)以及带flat/前缀的名称(如flat/recommended、flat/strict-type-checked)。后者以allFlat(plugin, parser)这种工厂函数的形式生成,是因为 ESLint 存在一个上游兼容性问题(源码注释指向 eslint/eslint#19513):flat 配置中需要把 parser 显式注入,而传统 eslintrc 配置则不需要。
rules对象来自 packages/eslint-plugin/src/rules/index.ts,文件逐个导入./rules目录下全部规则模块(约 130+ 个)并组成映射表,每个规则的键就是去掉@typescript-eslint/前缀的规则名。完整的规则清单在 packages/eslint-plugin/docs/rules/ 下每规则一份文档。
共享配置:从零到严格的一键式规则集
官方将插件内置配置称为“共享配置”(shared configs),完整用法见 docs/users/Shared_Configurations.mdx。这些配置分为两大类:代码正确性(correctness)配置与风格(stylistic)配置。绝大多数项目应优先从以下组合中选择起点:
recommended:即插即用的代码正确性规则,报告几乎总是坏实践或潜在 bug;recommended-type-checked:recommended+ 需要类型信息的推荐规则;strict:recommended+ 能抓 bug 但更有主见的规则;strict-type-checked:strict+ 需要类型信息的严格规则;stylistic/stylistic-type-checked:强制简洁一致代码风格的规则,不影响程序逻辑,且不会替代上面的正确性配置,而是叠加使用。
配置源码位于 packages/eslint-plugin/src/configs/,分eslintrc/(Legacy)与flat/(Flat)两个镜像目录。源码头部注释明确说明:这些文件由脚本自动生成,不要手工编辑,需要重新生成时执行pnpm run generate-configs(对应仓库脚本 tools/scripts/generate-configs.mts)。
Flat Config 用法
// @ts-check import js from '@eslint/js'; import { defineConfig } from 'eslint/config'; import tseslint from 'typescript-eslint'; export default defineConfig({ files: ['**/*.{js,ts}'], extends: [js.configs.recommended, tseslint.configs.recommended], });tseslint.configs.*下的字段名与配置名对应:recommended、recommendedTypeChecked、strict、strictTypeChecked、stylistic、stylisticTypeChecked等。extends是typescript-eslint聚合包中tseslint.config()/defineConfig提供的语法糖,底层实现见 packages/typescript-eslint/src/config-helper.ts——它会把extends中的配置展开拼进最终的配置数组。
Legacy(eslintrc)用法
module.exports = { extends: [ 'eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:@typescript-eslint/stylistic', ], };传统模式下通过plugin:@typescript-eslint/<配置名>字符串引用,需要显式安装本插件与@typescript-eslint/parser。
base:最小编成配置
flat/base(packages/eslint-plugin/src/configs/flat/base.ts)只做三件事:设置 parser、设置plugins['@typescript-eslint']、设置sourceType: 'module'。它是所有推荐配置自动包含的底座,官方不建议单独直接使用,而是从更高层的 recommended 配置继承。
eslint-recommended:与核心规则对齐
该配置解决一个实际问题:TypeScript 编译器本身会做大量静态检查,eslint:recommended中的若干核心规则在 TS 项目里是冗余甚至冲突的。因此 packages/eslint-plugin/src/configs/eslint-recommended-raw.ts 做了两件事:
- 关闭已被 TS 编译器覆盖的核心规则,并逐条标注对应的 TS 错误码,例如
constructor-super(ts(2335)/ts(2377))、no-dupe-class-members(ts(2393))、no-undef(ts(2304)/ts(2552))、no-unreachable(ts(7027))、no-redeclare(ts(2451))等; - 开启几条更贴合 TS 时代的规则:
no-var、prefer-const、prefer-rest-params、prefer-spread——因为 TS 的转译与类型推导让这些现代写法收益更大。
值得注意的实现细节:该文件同时被 Legacy 与 Flat 复用,通过style: 'glob' | 'minimatch'参数区分文件匹配语法(传统 eslintrc 用 glob,如*.ts;flat 用 minimatch,如**/*.ts)。eslint-recommended同样被所有推荐配置自动包含。
recommended的完整规则清单
以 flat 版本(packages/eslint-plugin/src/configs/flat/recommended.ts)为准,recommended由base+eslint-recommended+ 以下规则构成:
| 规则 | 严重度 | 作用 |
|---|---|---|
@typescript-eslint/ban-ts-comment | error | 禁止无意义的// @ts-xxx注释 |
@typescript-eslint/no-array-constructor | error(同时关闭核心no-array-constructor) | 禁止Array()构造器 |
@typescript-eslint/no-duplicate-enum-values | error | 禁止重复的枚举值 |
@typescript-eslint/no-empty-object-type | error | 禁止空对象类型{} |
@typescript-eslint/no-explicit-any | error | 禁止显式any |
@typescript-eslint/no-extra-non-null-assertion | error | 禁止多余的非空断言!! |
@typescript-eslint/no-misused-new | error | 禁止在接口中定义new/constructor之外的方法 |
@typescript-eslint/no-namespace | error | 禁止使用namespace/module |
@typescript-eslint/no-non-null-asserted-optional-chain | error | 禁止对可选链再使用! |
@typescript-eslint/no-require-imports | error | 禁止使用require |
@typescript-eslint/no-this-alias | error | 禁止对this赋值别名 |
@typescript-eslint/no-unnecessary-type-constraint | error | 禁止无意义的类型约束(如T extends any) |
@typescript-eslint/no-unsafe-declaration-merging | error | 禁止不安全的声明合并 |
@typescript-eslint/no-unsafe-function-type | error | 禁止裸Function类型 |
@typescript-eslint/no-unused-expressions | error(同时关闭核心no-unused-expressions) | 禁止无用表达式,并兼容 TS 语法 |
@typescript-eslint/no-unused-vars | error(同时关闭核心no-unused-vars) | 禁止未使用变量,兼容类型参数等 TS 场景 |
@typescript-eslint/no-wrapper-object-types | error | 禁止String/Number/Boolean等包装对象类型 |
@typescript-eslint/prefer-as-const | error | 优先使用as const |
@typescript-eslint/prefer-namespace-keyword | error | 优先使用namespace关键字 |
@typescript-eslint/triple-slash-reference | error | 禁止使用三斜线指令 |
strict与stylistic在 recommended 之上增加了什么
strict(packages/eslint-plugin/src/configs/flat/strict.ts)在 recommended 基础上追加:no-dynamic-delete、no-extraneous-class、no-invalid-void-type、no-non-null-asserted-nullish-coalescing、no-non-null-assertion、no-useless-constructor、prefer-literal-enum-member、unified-signatures,并把ban-ts-comment收紧为要求@ts注释必须带至少 10 个字符的描述({ minimumDescriptionLength: 10 })。
stylistic(packages/eslint-plugin/src/configs/flat/stylistic.ts)则追加:adjacent-overload-signatures、array-type、ban-tslint-comment、class-literal-property-style、consistent-generic-constructors、consistent-indexed-object-style、consistent-type-assertions、consistent-type-definitions、no-confusing-non-null-assertion、no-empty-function、no-inferrable-types、prefer-for-of、prefer-function-type。
官方建议:只有当团队中有相当比例的人对 TypeScript 足够熟练时,才把recommended升级为strict、把recommended-type-checked升级为strict-type-checked。
*-only变体与组合语义
配置矩阵中还有三个-only配置,用于“只取类型感知部分”:
recommended-type-checked-only:只包含类型感知规则及对应的核心规则关闭项;recommended+recommended-type-checked-only等价于recommended-type-checked(见 packages/eslint-plugin/src/configs/flat/recommended-type-checked-only.ts);strict-type-checked-only:同理,strict+ 它等价于strict-type-checked;stylistic-type-checked-only:同理,stylistic+ 它等价于stylistic-type-checked。
这种拆分便于在只需要类型感知规则、又想完全掌控非类型规则的项目中使用。
all与disable-type-checked:两个特例
all会启用插件提供的全部规则(packages/eslint-plugin/src/configs/flat/all.ts)。但官方明确不推荐直接使用:很多规则互相冲突或本应逐项目配置。disable-type-checked是一个工具型配置:把所有类型感知规则批量置为off,并把 parser 的parserOptions重置为{ program: null, project: false, projectService: false }(packages/eslint-plugin/src/configs/flat/disable-type-checked.ts)。典型场景是“整体开启类型感知 linting,但对某类文件(如**/*.js)用 override 局部关闭”,例如:
export default defineConfig( { files: ['**/*.{js,ts}'], extends: [js.configs.recommended, tseslint.configs.recommendedTypeChecked], languageOptions: { parserOptions: { projectService: true } }, }, { files: ['**/*.js'], extends: [tseslint.configs.disableTypeChecked], }, );类型感知规则(Typed Linting):开启方式与代价
部分规则会调用 TypeScript 的类型检查 API,把分析范围从“单个文件”扩大到“整个项目”,从而提供远超语法层面的洞察。这类规则的meta.docs中标注了requiresTypeChecking: true,例如 packages/eslint-plugin/src/rules/no-floating-promises.ts 的no-floating-promises(禁止未处理的 Promise)、no-unsafe-*系列、no-unnecessary-*系列、restrict-template-expressions等。
根据 docs/getting-started/Typed_Linting.mdx,启用类型感知 linting 只需要两处改动:
- 把使用的预置配置换成带
TypeChecked的版本(recommended→recommendedTypeChecked,strict→strictTypeChecked,stylistic→stylisticTypeChecked); - 配置
languageOptions.parserOptions,告诉 parser 如何为每个源文件找到对应 TSConfig。
import js from '@eslint/js'; import { defineConfig } from 'eslint/config'; import tseslint from 'typescript-eslint'; export default defineConfig({ files: ['**/*.{js,ts}'], extends: [ js.configs.recommended, tseslint.configs.recommendedTypeChecked, ], languageOptions: { parserOptions: { projectService: true, }, }, });其中parserOptions.projectService: true表示向 TypeScript 的类型检查服务按文件请求类型信息,是官方推荐的方式(替代更早的parserOptions.project指向 tsconfig 路径的写法);Legacy 配置下还需设置parserOptions.tsconfigRootDir指向项目根目录。
代价同样在文档中言明:类型感知规则要求 ESLint 运行前先让 TypeScript 对整个项目做一次“构建”,小项目耗时几秒以内,大项目可能明显变长。官方给出的权衡结论是强烈推荐使用类型感知 linting,并建议将完整的类型感知 lint 放在 CI / 推送前执行,IDE 场景则通过缓存规避大部分开销。更多排障见 docs/troubleshooting/typed-linting/index.mdx 与 docs/troubleshooting/typed-linting/Performance.mdx。
规则实现:从createRule到配置生成的完整链路
理解规则源码有助于读懂插件行为。所有规则都通过createRule工具(位于 packages/eslint-plugin/src/util/)定义。以典型的no-explicit-any(packages/eslint-plugin/src/rules/no-explicit-any.ts)为例,其结构为:
export default createRule<Options, MessageIds>({ name: 'no-explicit-any', meta: { type: 'suggestion', docs: { description: 'Disallow the `any` type', recommended: 'recommended', // 声明它属于哪个预置配置 }, fixable: 'code', // 支持自动修复 hasSuggestions: true, // 提供建议(suggestion)而非直接修复 messages: { /* 消息模板 */ }, schema: [{ /* 选项 schema,JSON Schema 格式 */ }], }, defaultOptions: [{ fixToUnknown: false, ignoreRestArgs: false }], create(context, [{ fixToUnknown, ignoreRestArgs }]) { /* 访问者逻辑 */ }, });no-explicit-any的可配置选项包括fixToUnknown(自动把any修复为unknown)与ignoreRestArgs(忽略 rest 参数数组),并额外提供never、PropertyKey、unknown三条替换建议——这些都是选项 schema 与hasSuggestions声明带来的能力。
meta.docs.recommended、requiresTypeChecking、extendsBaseRule这些元数据被上层工具消费:例如 packages/eslint-plugin/tests/configs.test.ts 会读取每条规则的元数据,校验规则名前缀、扩展规则(extendsBaseRule)、推荐程度与类型感知标记,从而保证预置配置与规则声明保持一致。规则功能测试则位于 packages/eslint-plugin/tests/rules/,使用工作区内的@typescript-eslint/rule-tester驱动。
类型声明方面,packages/eslint-plugin/rules.d.ts 用大段注释解释了为何插件不直接为每条规则生成独立的.d.ts:规则都用createRule(...)推断类型,声明文件生成会触发ts(2742)(无法在不引入新 import 的情况下命名推断类型,而新 import 可能带来类型副作用),因此以TypeScriptESLintRules记录类型加命名空间导出的方式做了“hacky”的类型兜底。这也是use-at-your-own-risk/rules入口存在的原因之一。
稳定性语义与注意事项
- 除
all、strict、strict-type-checked外,其余配置都被视为semver 稳定:规则的增删只会出现在大版本升级中(见 docs/users/Shared_Configurations.mdx)。 recommended-requiring-type-checking是recommended-type-checked的已废弃别名(见 packages/eslint-plugin/src/raw-plugin.ts 中的@deprecated注释),新项目应使用后者。- 所有预置配置都不包含格式化规则(只约束空白与排版等 trivial 内容的规则)。官方强烈建议用 Prettier 或等价工具负责格式化,而不是引入 ESLint 的格式化规则(见 docs/users/What_About_Formatting.mdx)。
结语
@typescript-eslint/eslint-plugin的价值在于把“TypeScript 的语法与类型知识”转化为 ESLint 可执行的规则体系:通过configs一键接入经过打磨的规则组合,通过rules获得可单独配置、可自动修复、可提供建议的细粒度规则对象,再配合projectService开启类型感知能力。想进一步深入,可以继续阅读 docs/getting-started/Quickstart.mdx(从零搭建)、docs/users/Shared_Configurations.mdx(配置全貌)与 docs/packages/ESLint_Plugin.mdx(插件官方文档),并在 packages/eslint-plugin/docs/rules/ 中按需查询每条规则的具体选项与示例。
【免费下载链接】typescript-eslint:sparkles: Monorepo for all the tooling which enables ESLint to support TypeScript项目地址: https://gitcode.com/GitHub_Trending/ty/typescript-eslint
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考