优化 TypeScript 开发:ESLint 设置与技巧 – wiki基地


优化 TypeScript 开发:ESLint 设置与技巧详解

TypeScript 以其静态类型检查、强大的面向对象特性以及对最新 ECMAScript 标准的支持,彻底改变了现代 Web 开发,尤其是在构建大型、复杂的应用程序时。然而,仅仅使用 TypeScript 编译器(tsc)进行类型检查是不够的。为了确保代码的质量、一致性、可维护性,并预防潜在的运行时错误,我们需要引入强大的代码检查工具——ESLint。

ESLint 作为 JavaScript 和 TypeScript 社区事实上的标准 Linter,提供了高度可配置的规则集,能够分析代码、发现问题并强制执行编码规范。结合专门为 TypeScript 设计的 @typescript-eslint 插件,ESLint 成为了优化 TypeScript 开发流程、提升团队协作效率和保障项目长期健康的关键一环。

本文将深入探讨如何配置和使用 ESLint 来优化 TypeScript 开发,涵盖从基础设置到高级技巧的方方面面,旨在帮助开发者充分利用 ESLint 的能力,编写出更健壮、更清晰、更易于维护的 TypeScript 代码。

一、 为什么 TypeScript 项目需要 ESLint?

有人可能会问:“TypeScript 本身不是已经有类型检查了吗?为什么还需要 ESLint?” 这是一个常见的疑问,但它忽略了 ESLint 在 TypeScript 开发中扮演的独特且互补的角色:

  1. 超越类型检查的代码质量分析tsc 主要负责确保类型安全,它不会过多关注代码风格、潜在的逻辑错误、未使用的变量(基础场景外)或某些可能导致性能问题的写法。ESLint 则可以填补这些空白,检查代码风格(如缩进、引号、命名约定)、发现潜在错误(如 no-unsafe-finally)、识别反模式(如 no-empty-function)以及强制执行最佳实践。
  2. 强制代码风格一致性:在团队协作中,统一的代码风格至关重要。ESLint 可以强制执行预设的或自定义的编码规范,确保所有成员提交的代码在格式、命名、结构等方面保持一致,极大地提高了代码的可读性和可维护性。
  3. 早期错误检测:许多潜在的运行时错误或逻辑缺陷可以在编码阶段就被 ESLint 发现。例如,@typescript-eslint/no-floating-promises 规则可以确保所有 Promise 都被正确处理(await.then().catch()),避免未捕获的 Promise rejections 导致应用程序崩溃。这比等到运行时或测试阶段才发现问题要高效得多。
  4. 利用 TypeScript 类型信息进行更深入的检查@typescript-eslint 插件的核心优势在于它能够利用 TypeScript 的类型信息进行更智能、更深入的 Linting。这被称为“类型感知 Linting”(Type-Aware Linting)。例如,@typescript-eslint/no-misused-promises 规则可以检查你是否在不应该使用 Promise 的地方(如 if 条件语句)错误地使用了它。这是纯 JavaScript Linter 无法做到的。
  5. 生态系统集成:ESLint 拥有庞大的生态系统,可以与各种编辑器(VS Code、WebStorm 等)、构建工具(Webpack、Vite)、版本控制工具(Husky + lint-staged)以及 CI/CD 流程无缝集成,实现自动化检查和修复。
  6. TSLint 的继任者:曾经流行的 TypeScript Linter TSLint 已被官方废弃,并推荐迁移到 ESLint + @typescript-eslint。社区资源和未来的发展都将围绕 ESLint 展开。

总之,tsc 负责“代码能否运行”(类型安全),而 ESLint 负责“代码写得好不好”(质量、风格、最佳实践)。两者结合,才能构建出高质量、高可维护性的 TypeScript 应用。

二、 搭建 TypeScript 项目的 ESLint 环境

为一个新的或现有的 TypeScript 项目配置 ESLint 通常涉及以下步骤:

1. 安装依赖

首先,你需要安装 ESLint 核心库以及 TypeScript 相关的解析器和插件。

“`bash

使用 npm

npm install eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin –save-dev

或者使用 yarn

yarn add eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin –dev

或者使用 pnpm

pnpm add eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin –save-dev
“`

  • eslint: ESLint 核心库。
  • @typescript-eslint/parser: 告诉 ESLint 如何解析 TypeScript 代码。它将 TypeScript 代码转换成 ESLint 能够理解的 AST(抽象语法树)。
  • @typescript-eslint/eslint-plugin: 包含一系列专门为 TypeScript 代码设计的 ESLint 规则。

2. 创建 ESLint 配置文件

在项目根目录下创建一个 ESLint 配置文件。常见的文件名有 .eslintrc.js, .eslintrc.cjs, .eslintrc.yaml, .eslintrc.yml, .eslintrc.json。推荐使用 .eslintrc.js.eslintrc.cjs,因为它们支持 JavaScript 逻辑(例如,动态配置)。

一个基础的 .eslintrc.js 配置如下:

“`javascript
// .eslintrc.js
module.exports = {
// 指定根目录标识,ESLint 会停止在父级目录中查找配置文件
root: true,

// 指定解析器
parser: ‘@typescript-eslint/parser’,

// 解析器选项
parserOptions: {
// 指定 ECMAScript 版本
ecmaVersion: ‘latest’, // 或指定具体年份如 2020, 2021, etc.
// 指定源码类型为 module (ECMAScript modules)
sourceType: ‘module’,
// !! 这是启用类型感知 Linting 的关键配置 !!
// 指定 TypeScript 配置文件的路径
// 通常指向项目根目录下的 tsconfig.json
// ESLint 会使用它来获取类型信息
project: ‘./tsconfig.json’,
// 你可能还需要配置 tsconfigRootDir,特别是当你的 tsconfig.json 不在项目根目录时
// tsconfigRootDir: __dirname,
},

// 指定插件
plugins: [
‘@typescript-eslint’,
// 你可能还会用到其他插件,如 ‘react’, ‘vue’, ‘prettier’ 等
],

// 扩展配置:继承预设的规则集
extends: [
// ESLint 官方推荐的基础规则集
‘eslint:recommended’,
// @typescript-eslint 推荐的适用于 TypeScript 的规则集
‘plugin:@typescript-eslint/recommended’,
// !! 推荐:包含需要类型信息的推荐规则 !!
// 这个配置会启用更多强大的、基于类型信息的规则
// 注意:这会稍微增加 Linting 时间
‘plugin:@typescript-eslint/recommended-requiring-type-checking’,
// 如果你使用 Prettier,需要添加 Prettier 的配置来禁用与 Prettier 冲突的 ESLint 规则
// ‘prettier’, // 旧版方式,现在推荐 eslint-config-prettier
// ‘plugin:prettier/recommended’, // 如果使用 eslint-plugin-prettier
],

// 环境设定:指定代码运行的环境,预定义全局变量
env: {
browser: true, // 如果代码运行在浏览器环境
node: true, // 如果代码运行在 Node.js 环境
es2021: true, // 启用 ES2021 全局变量和语法
},

// 自定义规则:覆盖或添加规则
// “off” or 0 – 关闭规则
// “warn” or 1 – 将规则视为一个警告(不会导致程序退出)
// “error” or 2 – 将规则视为一个错误(当被触发时,程序会退出)
rules: {
// 示例:关闭 console 警告 (在开发中可能需要打开)
‘no-console’: process.env.NODE_ENV === ‘production’ ? ‘warn’ : ‘off’,
// 示例:强制使用 === 和 !==
‘eqeqeq’: [‘error’, ‘always’],

// --- TypeScript 特有规则示例 ---
// 禁止使用 any 类型 (非常推荐开启,但可能需要逐步实施)
'@typescript-eslint/no-explicit-any': ['warn', { fixToUnknown: true }], // 警告级别,并提供 quick fix 转为 unknown
// 未使用的变量 (使用 TypeScript 版本以正确处理类型和接口)
'no-unused-vars': 'off', // 必须关闭 ESLint 原生规则
'@typescript-eslint/no-unused-vars': ['warn', { 'argsIgnorePattern': '^_' }], // 开启 TS 版本,忽略以下划线开头的参数
// 强制函数显式声明返回类型 (对于库或公共 API 很有用)
'@typescript-eslint/explicit-function-return-type': 'off', // 根据项目需求开启或关闭
// 强制模块导出函数和类的公共方法显式声明返回类型
'@typescript-eslint/explicit-module-boundary-types': 'warn', // 推荐开启
// 禁止使用非空断言 (!)
'@typescript-eslint/no-non-null-assertion': 'warn',
// 要求 await 后面必须是 Thenable 类型 (需要类型信息)
'@typescript-eslint/await-thenable': 'error',
// 禁止未处理的 Promise (需要类型信息)
'@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: true }], // ignoreVoid 允许 `void promise()` 写法
// 禁止在不恰当的地方使用 Promise (如 if 条件) (需要类型信息)
'@typescript-eslint/no-misused-promises': 'error',
// 禁止不必要的类型断言 (需要类型信息)
'@typescript-eslint/no-unnecessary-type-assertion': 'warn',

// ... 更多规则配置

},

// 针对特定文件或目录的覆盖配置
overrides: [
{
files: [‘.js’, ‘.cjs’], // 对 JS 文件禁用 TypeScript 规则
rules: {
‘@typescript-eslint/explicit-function-return-type’: ‘off’,
‘@typescript-eslint/explicit-module-boundary-types’: ‘off’,
// 如果 JS 文件不需要类型检查相关的规则,可以在这里禁用
‘@typescript-eslint/no-unsafe-assignment’: ‘off’,
‘@typescript-eslint/no-unsafe-call’: ‘off’,
// … 其他仅适用于 TS 的规则
},
},
{
files: [‘tests//*.ts’, ‘tests//*.tsx’], // 对测试文件放宽某些规则
env: {
jest: true, // 或 mocha: true 等
},
rules: {
‘@typescript-eslint/no-explicit-any’: ‘off’, // 测试中可能允许使用 any
‘@typescript-eslint/no-non-null-assertion’: ‘off’, // 测试中可能需要使用非空断言
},
},
],

// 全局变量声明 (不推荐,尽量使用 import)
// globals: {
// myGlobalVar: ‘readonly’, // ‘writable’ 或 ‘readonly’
// },

// 忽略检查的文件或目录模式
ignorePatterns: [
‘node_modules/’,
‘dist/’,
‘build/’,
‘coverage/’,
‘*.config.js’, // 忽略配置文件
‘.eslintrc.js’, // 忽略 ESLint 配置文件本身
// … 其他需要忽略的文件或目录
],
};
“`

关键配置项说明:

  • root: true: 防止 ESLint 向上查找父目录的配置文件,确保项目配置的独立性。
  • parser: '@typescript-eslint/parser': 指定使用 TypeScript 解析器。
  • parserOptions.project: 这是启用类型感知 Linting 的核心。它告诉 ESLint tsconfig.json 文件的位置,以便 @typescript-eslint/parser 能够利用 TypeScript 程序实例来获取类型信息。没有这个配置,依赖类型信息的规则(如 no-floating-promises, no-misused-promises)将无法工作。
  • plugins: ['@typescript-eslint']: 加载 @typescript-eslint 插件,使其提供的规则可用。
  • extends: 继承预设的规则集。这是一个非常重要的配置,可以让你快速启用一系列推荐的最佳实践规则。
    • eslint:recommended: ESLint 官方推荐的基础规则。
    • plugin:@typescript-eslint/recommended: @typescript-eslint 推荐的不需要类型信息的规则。
    • plugin:@typescript-eslint/recommended-requiring-type-checking: @typescript-eslint 推荐的需要类型信息的规则。强烈推荐启用,这是发挥 @typescript-eslint 最大威力的关键,但请注意它会增加 Linting 的时间,因为它需要执行完整的类型检查。
  • rules: 自定义规则的启用、禁用和配置。规则的优先级高于 extends 中继承的规则。
  • env: 定义代码的运行环境,告知 ESLint 哪些全局变量是预定义的(如 browser 环境下的 windownode 环境下的 process)。
  • overrides: 允许你为特定的文件(基于 glob 模式)应用不同的配置。这对于区分对待源代码、测试代码、配置文件等非常有用。
  • ignorePatterns: 指定 ESLint 应该忽略检查的文件和目录。通常包括构建产物、依赖项等。

3. 配置 .eslintignore 文件 (可选但推荐)

虽然可以在 .eslintrc.js 中使用 ignorePatterns,但创建一个单独的 .eslintignore 文件(语法类似 .gitignore)通常更清晰,特别是当忽略列表很长时。

“`

.eslintignore

node_modules
dist
build
coverage
public
.log
.lock
“`

4. 添加运行脚本到 package.json

为了方便运行 ESLint,可以在 package.jsonscripts 部分添加命令:

json
{
"scripts": {
"lint": "eslint . --ext .ts,.tsx --report-unused-disable-directives --max-warnings 0",
"lint:fix": "eslint . --ext .ts,.tsx --fix"
}
}

  • eslint .: 对当前目录及其子目录进行 Linting。
  • --ext .ts,.tsx: 指定要检查的文件扩展名。对于包含 JSX 的 TypeScript 文件,需要添加 .tsx
  • --report-unused-disable-directives: 报告未使用的 eslint-disable 注释,有助于清理不再需要的禁用指令。
  • --max-warnings 0: 将警告视为错误,确保 CI 流程在有任何问题时失败。可以根据团队策略调整。
  • --fix: 尝试自动修复 ESLint 发现的可修复问题。

现在,你可以通过 npm run lintyarn lint 来检查代码,通过 npm run lint:fixyarn lint:fix 来尝试自动修复。

5. IDE 集成

为了获得最佳的开发体验,务必在你的 IDE 中安装并配置 ESLint 插件(例如,VS Code 的 “ESLint” 扩展)。这样可以在你编写代码时实时显示错误和警告,并提供快速修复选项。确保 IDE 使用项目本地安装的 ESLint 版本和配置。

三、 优化 TypeScript 开发的关键 ESLint 规则

@typescript-eslint 提供了大量规则,以下是一些对优化 TypeScript 开发特别有价值的规则类别和示例:

1. 利用类型信息的强大规则 (Type-Aware Rules)

这些规则是 @typescript-eslint 的精髓,需要 parserOptions.project 配置才能工作。

  • @typescript-eslint/no-floating-promises: (强烈推荐) 防止 Promise 被遗忘处理。未处理的 Promise rejection 可能导致难以追踪的运行时错误。
    “`typescript
    // Bad
    async function doSomethingAsync() { // }
    doSomethingAsync(); // Error: Promises must be handled appropriately

    // Good
    async function main() {
    await doSomethingAsync();
    // or
    doSomethingAsync().catch(err => console.error(err));
    // or using void operator if intentionally ignoring
    void doSomethingAsync(); // Requires ignoreVoid: true in rule config
    }
    * `@typescript-eslint/no-misused-promises`: **(强烈推荐)** 确保 Promise 不会被用在同步逻辑的位置,如 `if` 条件、数组 `forEach` 回调等。typescript
    // Bad
    async function isReady() { return true; }
    if (isReady()) { // } // Error: Promise returned in condition

    // Good
    async function check() {
    if (await isReady()) { // }
    }
    ``
    *
    @typescript-eslint/await-thenable: 强制对任何看起来像 Promise(具有then方法)的值使用await
    *
    @typescript-eslint/no-unnecessary-type-assertion: 发现并警告不必要的类型断言(as Type),鼓励更安全的类型推断或类型守卫。
    *
    @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-argument: **(核心安全规则)** 这一系列规则旨在严格控制any类型的使用和传播。当一个值为any类型时,TypeScript 基本上放弃了对它的类型检查。这些规则会标记将any赋值给明确类型的变量、调用any类型的值、访问any类型的成员等不安全操作,迫使开发者处理any,通常通过类型断言(应谨慎使用)、类型守卫或将其转换为unknown来进行更安全的处理。启用这些规则是迈向更健壮类型安全的重要一步。
    *
    @typescript-eslint/strict-boolean-expressions: 要求在条件表达式中(如if,while,&&,||)使用的值必须是明确的布尔类型,或者进行显式比较。这有助于避免对null,undefined,0,等“假值”的意外依赖。可以配置允许哪些类型。
    *
    @typescript-eslint/prefer-reduce-type-parameter: 当使用Array#reduce` 时,推荐提供泛型类型参数,而不是对累加器进行类型断言,以提高类型安全性。

2. 代码风格与一致性规则

  • @typescript-eslint/naming-convention: (高度可定制) 非常强大的规则,用于强制执行各种标识符(变量、函数、类、接口、类型别名、枚举成员等)的命名约定。可以精细配置大小写(camelCase, PascalCase, snake_case, UPPER_CASE)、前缀、后缀等。
    javascript
    // .eslintrc.js -> rules
    '@typescript-eslint/naming-convention': [
    'error',
    { selector: 'variable', format: ['camelCase', 'UPPER_CASE', 'PascalCase'] }, // 允许 const MyComponent = ...
    { selector: 'function', format: ['camelCase'] },
    { selector: 'parameter', format: ['camelCase'], leadingUnderscore: 'allow' }, // 允许 _unusedParam
    { selector: 'typeLike', format: ['PascalCase'] }, // 类, 接口, 类型别名, 枚举
    { selector: 'interface', format: ['PascalCase'], custom: { regex: '^I[A-Z]', match: false } }, // 禁止接口以 'I' 开头
    { selector: 'enumMember', format: ['UPPER_CASE'] },
    ],
  • @typescript-eslint/explicit-function-return-type: 要求函数显式声明返回类型。对于库作者或需要清晰 API 边界的情况很有用,但对于应用内部函数可能过于冗余,因为 TS 通常能很好地推断。
  • @typescript-eslint/explicit-module-boundary-types: 类似于上一条,但仅限于模块导出的函数和类的公共方法。这通常是一个更好的折衷,确保模块的公共接口类型清晰。
  • @typescript-eslint/no-inferrable-types: 禁止为可以被 TypeScript 轻松推断出来的变量或参数添加类型注解,避免代码冗余。
    ``typescript
    // Bad
    const x: number = 5;
    function greet(name: string = 'world'): string { return
    Hello, ${name}`; }

    // Good (if types are easily inferrable)
    const x = 5;
    function greet(name = ‘world’) { return Hello, ${name}; }
    ``
    *
    @typescript-eslint/consistent-type-imports: 强制使用import type { … } from …语法来导入类型,使类型导入更加清晰,并可能有助于某些构建工具进行优化。
    *
    @typescript-eslint/consistent-type-definitions: 强制使用interfacetype来定义对象类型,保持一致性。通常推荐优先使用interface定义对象结构,type` 用于联合类型、交叉类型、元组等。

3. 避免潜在问题和反模式的规则

  • @typescript-eslint/no-explicit-any: (重要) 禁止显式使用 any 类型。any 会破坏 TypeScript 的类型安全。建议将其设置为 warnerror,并尽可能使用更具体的类型或 unknown
  • @typescript-eslint/no-unused-vars: (基础) 发现未使用的变量、函数、类型、接口等。务必使用 @typescript-eslint 提供的版本 (@typescript-eslint/no-unused-vars) 并禁用 ESLint 原生版本 (no-unused-vars),因为它能正确处理 TypeScript 的类型声明和装饰器等。
  • @typescript-eslint/no-non-null-assertion: 禁止使用非空断言操作符 (!)。非空断言告诉编译器“我知道这个值在这里肯定不是 null 或 undefined”,但如果判断错误,会在运行时导致错误。鼓励使用类型守卫或可选链 (?.) / 空值合并 (??) 来代替。
  • @typescript-eslint/no-empty-interface: 禁止定义空的接口 (interface Foo {})。通常一个空接口没有实际意义,可能是遗留代码或类型定义不完整。
  • @typescript-eslint/no-namespace: 推荐使用 ES6 模块 (import/export) 而不是 TypeScript 的 namespace(内部模块),以符合现代 JavaScript 标准和工具链。
  • @typescript-eslint/prefer-nullish-coalescing: 推荐使用空值合并运算符 (??) 代替逻辑或 (||) 来提供默认值,因为 ?? 只在左侧为 nullundefined 时返回右侧值,而 || 会对所有“假值”(包括 0, false, '')返回右侧值,有时可能导致意外行为。 (需要类型信息)
  • @typescript-eslint/prefer-optional-chain: 推荐使用可选链操作符 (?.) 来访问可能为 nullundefined 的对象的属性或方法,使代码更简洁、更安全。 (需要类型信息)

选择和配置规则的策略:

  • 从推荐集开始: eslint:recommended, plugin:@typescript-eslint/recommended, plugin:@typescript-eslint/recommended-requiring-type-checking 是很好的起点。
  • 渐进式增强: 不要试图一次性启用所有严格规则,特别是对于现有的大型项目。可以先将一些严格规则(如 no-explicit-any, no-non-null-assertion)设置为 warn 级别,逐步修复,然后再提升到 error 级别。
  • 团队讨论: 规则的选择,特别是风格相关的规则(如命名约定、返回类型注解),应该由团队共同讨论决定,并记录在文档中。
  • 考虑项目类型: 库项目可能比应用项目需要更严格的规则(如强制导出类型注解)。
  • 平衡利弊: 某些规则(如强制返回类型)可能会增加代码冗余,需要权衡其带来的清晰度和维护成本。

四、 高级技巧与集成

1. ESLint 与 Prettier 协同工作

ESLint 负责代码质量和部分格式化,而 Prettier 是一个专注于代码格式化的、更“固执”的工具。两者结合可以达到最佳效果:ESLint 保证代码质量,Prettier 统一代码外观。

集成步骤:

  • 安装 Prettier 和相关集成包:
    bash
    npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier
    # or
    yarn add --dev prettier eslint-config-prettier eslint-plugin-prettier
  • eslint-config-prettier: 这个包的作用是 关闭 ESLint 中所有与 Prettier 可能冲突的格式化规则。将其添加到 .eslintrc.jsextends 数组的 最后,确保它覆盖掉其他配置中的冲突规则。
    javascript
    // .eslintrc.js
    module.exports = {
    extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:@typescript-eslint/recommended-requiring-type-checking',
    // !! 确保 prettier 配置在最后 !!
    'prettier', // 或者 'eslint-config-prettier' 的完整路径
    ],
    // ...
    };
  • eslint-plugin-prettier (可选但推荐): 这个插件将 Prettier 作为 ESLint 的一个规则来运行。这样,运行 eslint --fix 时也能应用 Prettier 的格式化。
    • 将其添加到 plugins 数组。
    • extends 中添加 'plugin:prettier/recommended',它会自动执行 plugins: ['prettier'], 设置 'prettier/prettier': 'error' 规则, 并应用 eslint-config-prettier
      javascript
      // .eslintrc.js using plugin:prettier/recommended
      module.exports = {
      extends: [
      'eslint:recommended',
      'plugin:@typescript-eslint/recommended',
      'plugin:@typescript-eslint/recommended-requiring-type-checking',
      // !! 这个会自动应用 eslint-config-prettier 并启用 prettier 规则 !!
      'plugin:prettier/recommended',
      ],
      plugins: [
      '@typescript-eslint',
      // 'prettier' 已经被 plugin:prettier/recommended 包含
      ],
      rules: {
      // 可以覆盖 prettier 规则的选项,但不推荐,最好使用 .prettierrc 文件
      // 'prettier/prettier': ['error', { 'singleQuote': true, 'semi': false }],
      },
      // ...
      };
  • 创建 Prettier 配置文件 .prettierrc.js.prettierrc.json 来定义你的格式化偏好。

通过这种方式,你可以让 Prettier 专注于代码格式,而 ESLint 专注于代码质量和 TypeScript 特有的检查。

2. 使用 Lint-Staged 和 Husky 实现 Git Hooks

为了确保提交到代码库的代码都符合规范,可以在 Git 的 pre-commit 钩子中运行 ESLint。husky 用于管理 Git 钩子,lint-staged 则允许你只对暂存区(staged)的文件运行 Linter 和 Formatter,提高效率。

  • 安装:
    bash
    npm install --save-dev husky lint-staged
    # or
    yarn add --dev husky lint-staged
  • 配置 husky (通常通过 npx husky installnpx husky add .husky/pre-commit "npx lint-staged" 来设置)。
  • package.json 或独立的配置文件 (.lintstagedrc.js 等) 中配置 lint-staged
    json
    // package.json
    {
    "lint-staged": {
    "*.{ts,tsx}": [
    "eslint --fix",
    "prettier --write" // 如果 Prettier 单独运行或作为 ESLint 插件效果不佳时
    ],
    "*.{js,jsx,json,css,md}": "prettier --write"
    }
    }

    这个配置表示:在提交时,对所有暂存的 .ts.tsx 文件运行 eslint --fix,然后运行 prettier --write。对其他类型的文件只运行 Prettier。

这样,每次提交前,只有被修改并暂存的文件会经过检查和自动修复,保证了代码库的整洁,同时避免了对整个项目运行 Linter 的性能开销。

3. CI/CD 集成

将 ESLint 检查集成到你的持续集成(CI)流程中(如 GitHub Actions, GitLab CI, Jenkins)是保障代码质量的最后一道防线。

示例 GitHub Action workflow 片段:

“`yaml
name: Build and Test

on: [push, pull_request]

jobs:
lint:
runs-on: ubuntu-latest
steps:
– uses: actions/checkout@v3
– name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: ’18’ # 或者你的项目所需版本
cache: ‘npm’ # 或 ‘yarn’, ‘pnpm’

  - name: Install dependencies
    run: npm ci # 使用 ci 通常更快更可靠

  - name: Run ESLint
    run: npm run lint # 运行 package.json 中定义的 lint 脚本
    # 确保 lint 脚本在遇到错误时会以非零状态码退出 (例如,使用 --max-warnings 0)

“`

4. 性能优化

  • 启用 ESLint 缓存: ESLint 支持缓存已检查的文件结果,避免重复检查未更改的文件。通过命令行参数 --cache 或在配置文件中设置 cache: true (某些配置方式支持) 来启用。缓存文件默认存储在 .eslintcache。记得将 .eslintcache 添加到 .gitignore
  • 关注类型感知规则的性能: 启用 recommended-requiring-type-checking 会增加 Linting 时间,因为它需要 tsc 构建完整的程序类型信息。对于非常大的项目,如果本地开发或 CI Linting 速度成为瓶颈:
    • 考虑在本地开发时,只运行速度更快的非类型感知规则子集,而在 CI 中运行完整的规则集。
    • 确保 tsconfig.jsonincludeexclude 配置尽可能精确,避免包含不必要的文件。
    • 在 CI 中,如果可能,只 Lint 变更的文件(类似于 lint-staged 的逻辑,但应用于 CI 环境)。
  • 定期更新依赖: 保持 eslint, @typescript-eslint/parser, @typescript-eslint/eslint-plugin 等库为最新版本,通常会包含性能改进和 Bug 修复。

5. 自定义规则

对于团队特有的、现有规则无法满足的编码规范或模式,可以考虑编写自定义 ESLint 规则。这需要对 ESLint 的工作原理和 AST 有一定的了解,但能提供极高的灵活性。

五、 常见问题与故障排查

  • Parser Errors: 最常见的是 parserOptions.project 配置不正确或缺失。确保路径指向正确的 tsconfig.json 文件。有时也可能是 ESLint 插件/解析器版本与 TypeScript 版本不兼容,检查并更新。错误信息 “The file must be included in at least one of the projects provided” 通常意味着当前检查的文件没有被 tsconfig.jsoninclude 覆盖。
  • 规则冲突: 特别是在使用 Prettier 时,确保 eslint-config-prettierextends 数组的最后。检查 .eslintrc.js 中的 extends 顺序和 rules 配置,看是否有相互覆盖或冲突的设置。
  • 性能缓慢: 检查是否启用了大量类型感知的规则。尝试使用缓存 (--cache)。分析 tsconfig.json 的包含范围。考虑分阶段 Linting(本地 vs CI)。
  • any 类型泛滥: 如果项目中 any 过多导致 @typescript-eslint/no-unsafe-* 规则报大量错误,不要直接禁用规则。应该将其设为 warn,然后逐步修复,将 any 替换为具体的类型、unknown 或使用类型守卫。

结论

ESLint 结合 @typescript-eslint 是优化 TypeScript 开发流程、提升代码质量和保障项目可维护性的核心工具。它不仅弥补了 TypeScript 编译器在代码风格和潜在逻辑错误检查上的不足,更通过类型感知 Linting 提供了针对 TypeScript 的深度分析能力。

通过仔细配置 ESLint 环境,选择合适的规则集(特别是强大的类型感知规则),并将其与 Prettier、Git Hooks 和 CI/CD 流程有效集成,开发团队可以建立起一套自动化、标准化的代码质量保障体系。

虽然初期的配置和规则选择需要投入一些时间和精力,但这笔投资将带来长期的回报:减少 Bug、提高代码可读性、加速新成员融入、降低维护成本,并最终帮助团队更自信、更高效地构建出稳定、健壮的 TypeScript 应用程序。持续关注 @typescript-eslint 的更新,并根据项目的发展不断调整和优化 ESLint 配置,是保持开发流程高效和代码库健康的必要实践。


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部