Skip to content

ESLint

ESLint 是一个用于识别和报告 JavaScript 代码中模式的静态代码分析工具,它的主要目标是确保代码的一致性和避免错误。它由 Nicholas C. Zakas 于 2013 年创建,如今已成为前端开发中不可或缺的工具之一。

核心功能

  1. 代码检查:检测代码中的语法错误、潜在问题和不符合规范的写法
  2. 代码风格约束:确保团队遵循一致的代码风格(如缩进、命名规范等)
  3. 可扩展性:支持自定义规则和插件,适应不同项目需求
  4. 自动化修复:对部分问题提供自动修复功能(--fix 选项)

工作原理

ESLint 通过将代码解析为抽象语法树(AST),然后根据预定义的规则对 AST 进行检查,从而发现代码中的问题。它使用 Espree 作为默认解析器,也支持其他解析器(如 @babel/eslint-parser 用于支持 ES6+ 特性)。

基本使用流程

安装

shell
# 局部安装(推荐)
npm install eslint --save-dev

# 全局安装
npm install eslint -g

初始化配置文件

shell
npx eslint --init

检查文件

shell
# 检查单个文件
npx eslint your-file.js

# 检查目录
npx eslint src/

# 自动修复可修复的问题
npx eslint your-file.js --fix

规则级别

ESLint 规则有三个级别:

  • off0:关闭规则
  • warn1:开启规则,作为警告(不影响退出码)
  • error2:开启规则,作为错误(触发时退出码为 1)

常用扩展规则

为了简化配置,ESLint 社区提供了许多预设配置:

  • eslint:recommended:ESLint 官方推荐的核心规则
  • airbnb-base:Airbnb 公司的基础 JavaScript 风格指南
  • standard:JavaScript Standard Style
  • google:Google 的代码风格

使用这些配置要先安装相应的包

shell
npm install eslint-config-airbnb-base --save-dev

然后在配置文件中引用

javascript
module.exports = {
    "extends": "airbnb-base"
}

配置

ESLint 详细配置
TypeScript
import type {Linter} from 'eslint';

const config: Linter.Config = {
    // 指定解析器,用于解析TypeScript代码
    parser: '@typescript-eslint/parser',

    // 解析器选项,指定TypeScript的版本和模块系统
    parserOptions: {
        // 指定ECMAScript版本,2020是比较新的稳定版本
        ecmaVersion: 2020,
        // 指定模块系统,使用ES模块
        sourceType: 'module',
        // 额外的语言特性
        ecmaFeatures: {
            // 允许使用JSX
            jsx: true
        },
        // 指定tsconfig.json路径,用于类型检查
        project: './tsconfig.json'
    },

    // 配置文件的继承,扩展已有的规则集
    extends: [
        // ESLint的基础规则
        'eslint:recommended',
        // TypeScript的推荐规则
        'plugin:@typescript-eslint/recommended',
        // TypeScript的严格规则,提供更严格的类型检查
        'plugin:@typescript-eslint/recommended-requiring-type-checking',
        // 禁用与Prettier冲突的规则
        'eslint-config-prettier',
        // 将Prettier作为ESLint规则运行
        'plugin:prettier/recommended'
    ],

    // 插件配置,引入需要的ESLint插件
    plugins: [
        // TypeScript的ESLint插件
        '@typescript-eslint',
        // 导入/导出相关的规则插件
        'import',
        // 促进代码可维护性的插件
        'sonarjs',
        // 检测未使用代码的插件
        'unused-imports'
    ],

    // 环境配置,指定代码运行的环境
    env: {
        // 浏览器环境
        browser: true,
        // Node.js环境
        node: true,
        // ES6环境
        es6: true
    },

    // 全局变量配置,指定可以使用的全局变量
    globals: {
        React: 'writable'
    },

    // 自定义规则配置
    rules: {
        // ==============================================
        // 基础ESLint规则
        // ==============================================

        // 禁止使用console,生产环境可以设为error
        'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'warn',

        // 禁止使用debugger
        'no-debugger': 'error',

        // 禁止未使用的变量,让@typescript-eslint/no-unused-vars接管
        'no-unused-vars': 'off',

        // 禁止在条件语句中使用赋值操作
        'no-cond-assign': ['error', 'always'],

        // 禁止在代码中使用eval()
        'no-eval': 'error',

        // 禁止扩展原生对象
        'no-extend-native': 'error',

        // 禁止不必要的括号
        'no-extra-parens': ['error', 'functions'],

        // 禁止在循环中使用函数
        'no-loop-func': 'error',

        // 禁止多次声明同一变量
        'no-redeclare': 'error',

        // 禁止在return语句中使用赋值
        'no-return-assign': ['error', 'always'],

        // 禁止自我赋值
        'no-self-assign': 'error',

        // 禁止自我比较
        'no-self-compare': 'error',

        // 禁止使用逗号操作符
        'no-sequences': 'error',

        // 禁止抛出非异常对象
        'no-throw-literal': 'error',

        // 禁止在finally块中使用return、throw等
        'no-unsafe-finally': 'error',

        // 禁止使用未定义的变量
        'no-undef': 'error',

        // 禁止在表达式中使用void
        'no-void': 'error',

        // 强制使用 === 和 !==
        'eqeqeq': ['error', 'always'],

        // 强制使用大括号包裹块级代码
        'curly': ['error', 'all'],

        // 强制switch语句中有default分支
        'default-case': 'error',

        // 强制使用一致的换行风格
        'linebreak-style': ['error', 'unix'],

        // 强制使用严格模式
        'strict': ['error', 'global'],

        // 强制正确使用typeof检查
        'valid-typeof': 'error',

        // ==============================================
        // TypeScript特定规则
        // ==============================================

        // 允许使用any类型,但给出警告
        '@typescript-eslint/no-explicit-any': 'warn',

        // 禁止未使用的变量,比基础规则更强大
        '@typescript-eslint/no-unused-vars': [
            'error',
            {
                argsIgnorePattern: '^_', // 忽略下划线开头的参数
                varsIgnorePattern: '^_', // 忽略下划线开头的变量
                caughtErrorsIgnorePattern: '^_' // 忽略下划线开头的捕获错误
            }
        ],

        // 允许使用require语句
        '@typescript-eslint/no-var-requires': 'off',

        // 允许使用非空断言
        '@typescript-eslint/no-non-null-assertion': 'off',

        // 禁止使用@ts-ignore注释
        '@typescript-eslint/ban-ts-comment': [
            'error',
            {
                'ts-ignore': 'allow-with-description' // 允许带描述的@ts-ignore
            }
        ],

        // 强制函数有返回类型注释
        '@typescript-eslint/explicit-function-return-type': [
            'warn',
            {
                allowExpressions: true, // 允许表达式中的函数没有返回类型
                allowTypedFunctionExpressions: true, // 允许有类型的函数表达式
                allowHigherOrderFunctions: true // 允许高阶函数
            }
        ],

        // 强制类成员有访问修饰符
        '@typescript-eslint/explicit-member-accessibility': [
            'error',
            {
                accessibility: 'explicit',
                overrides: {
                    constructors: 'off' // 构造函数可以没有访问修饰符
                }
            }
        ],

        // 禁止使用过时的类型断言语法
        '@typescript-eslint/consistent-type-assertions': [
            'error',
            {
                assertionStyle: 'as', // 使用as语法
                objectLiteralTypeAssertions: 'allow-as-parameter'
            }
        ],

        // 强制一致的类型导入风格
        '@typescript-eslint/consistent-type-imports': [
            'error',
            {
                prefer: 'type-imports', // 优先使用type导入
                fixStyle: 'separate-type-imports' // 分离类型导入
            }
        ],

        // 禁止使用容易混淆的非空断言和可选链
        '@typescript-eslint/no-non-null-asserted-optional-chain': 'error',

        // 禁止在条件中使用常量表达式
        '@typescript-eslint/no-constant-condition': 'error',

        // 禁止使用空函数
        '@typescript-eslint/no-empty-function': [
            'error',
            {
                allow: ['arrowFunctions', 'functions', 'methods'] // 允许某些空函数
            }
        ],

        // ==============================================
        // import插件规则
        // ==============================================

        // 强制导入顺序
        'import/order': [
            'error',
            {
                groups: [
                    ['builtin', 'external'], // 内置和外部模块
                    'internal', // 内部模块
                    ['parent', 'sibling', 'index'] // 相对路径模块
                ],
                'newlines-between': 'always', // 组之间必须有空行
                alphabetize: {
                    order: 'asc', // 按字母升序排列
                    caseInsensitive: true // 不区分大小写
                }
            }
        ],

        // 禁止重复导入
        'import/no-duplicates': 'error',

        // 禁止未使用的导入
        'unused-imports/no-unused-imports': 'error',

        // ==============================================
        // sonarjs插件规则
        // ==============================================

        // 禁止重复代码块
        'sonarjs/no-duplicate-string': ['warn', 5], // 字符串长度超过5才检查

        // 禁止复杂度高的函数
        'sonarjs/cognitive-complexity': ['error', 15], // 复杂度阈值15

        // 禁止重复的条件
        'sonarjs/no-duplicated-branches': 'error',

        // 禁止冗余的布尔表达式
        'sonarjs/no-redundant-boolean': 'error'
    },

    // 针对特定文件的覆盖配置
    overrides: [
        {
            // 测试文件
            files: ['**/*.test.ts', '**/*.spec.ts'],
            env: {
                jest: true // 启用Jest环境
            },
            rules: {
                // 测试文件放宽某些规则
                '@typescript-eslint/explicit-function-return-type': 'off',
                'no-console': 'off'
            }
        },
        {
            // 配置文件
            files: ['*.config.ts', '*.config.js'],
            rules: {
                // 配置文件放宽某些规则
                '@typescript-eslint/no-var-requires': 'off'
            }
        }
    ]
};

export default config;

尘埃虽微,积之成集;问题虽小,记之为鉴。 雾中低语,心之所向;思绪飘渺,皆可成章。