ESLint
ESLint 是一个用于识别和报告 JavaScript 代码中模式的静态代码分析工具,它的主要目标是确保代码的一致性和避免错误。它由 Nicholas C. Zakas 于 2013 年创建,如今已成为前端开发中不可或缺的工具之一。
核心功能
- 代码检查:检测代码中的语法错误、潜在问题和不符合规范的写法
- 代码风格约束:确保团队遵循一致的代码风格(如缩进、命名规范等)
- 可扩展性:支持自定义规则和插件,适应不同项目需求
- 自动化修复:对部分问题提供自动修复功能(--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 规则有三个级别:
off
或0
:关闭规则warn
或1
:开启规则,作为警告(不影响退出码)error
或2
:开启规则,作为错误(触发时退出码为 1)
常用扩展规则
为了简化配置,ESLint 社区提供了许多预设配置:
eslint:recommended
:ESLint 官方推荐的核心规则airbnb-base
:Airbnb 公司的基础 JavaScript 风格指南standard
:JavaScript Standard Stylegoogle
: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;