给接手历史项目配置ESLint+Prettier,发现还是会踩坑

一 添加ESLint+Prettier代码校验与格式化功能

1.1 安装ESLint和Prettier相关npm工具包

bash 复制代码
pnpm add -D eslint jiti eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a11y @typescript-eslint/eslint-plugin @typescript-eslint/parser @eslint/js @types/eslint-plugin-jsx-a11y globals


pnpm add -D prettier eslint-config-prettier eslint-plugin-prettier

这些包的用途如下:

npm包名 用途
eslint-plugin-react-hooks 提供 React Hooks 相关的 ESLint 插件,确保符合 React Hooks 的最佳实践。
eslint-plugin-react 提供 React 相关的 ESLint 插件,包含 React 特有的最佳实践和规则
@typescript-eslint/parser 允许 ESLint 解析 TypeScript 代码,并为 ESLint 提供语法分析功能。
@typescript-eslint/eslint-plugin 是 TypeScript 的 ESLint 插件,提供了一些 TypeScript 特有的规则。
@eslint/js 是 ESLint 官方的 JavaScript 规则集,提供了一些基础的 JavaScript 规则
jiti jiti 允许你使用 eslint.config.js 这样的 ESM 格式配置文件,而不是传统的 .eslintrc.js。主要用于支持 flat config(ESLint 9+ 采用的新配置方式)
eslint-plugin-jsx-a11y Airbnb主要维护,JSX 无障碍性社区贡献和支持
eslint-config-prettier 用于关闭 ESLint 规则中与 Prettier 冲突的格式化规则,避免 ESLint 和 Prettier 冲突
eslint-plugin-prettier 把 Prettier 作为 ESLint 规则,让 ESLint 直接检查 Prettier 规则

配置 eslint.config.ts

ESLint9和ESLint9之间的版本配置不太一样,最大的变化是配置内容由对象格式变成了扁平数组结构:

ts 复制代码
import js from '@eslint/js';
import tsPlugin from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import reactPlugin from 'eslint-plugin-react';
import reactHooksPlugin from 'eslint-plugin-react-hooks';
import prettierPlugin from 'eslint-plugin-prettier';

export default [
  js.configs.recommended, // JavaScript 推荐规则
  {
    files: ['**/*.ts', '**/*.tsx'],
    languageOptions: {
      parser: tsParser,
      parserOptions: {
        project: './tsconfig.json',
        ecmaVersion: 2020,
        sourceType: 'module',
      },
    },
    plugins: {
      '@typescript-eslint': tsPlugin, // 使用已导入的插件对象
      react: reactPlugin, // 使用已导入的插件对象
      'react-hooks': reactHooksPlugin, // 使用已导入的插件对象
      prettier: prettierPlugin, // 使用已导入的插件对象
    },
    rules: {
      'react/prop-types': 'off', // TypeScript 不需要 PropTypes
      '@typescript-eslint/no-explicit-any': 'error', // 禁止使用 any
      '@typescript-eslint/explicit-function-return-type': 'warn', // 要求显式返回类型
      '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], // 忽略未使用变量
      '@typescript-eslint/no-non-null-assertion': 'warn', // 避免不安全的非空断言
      'prettier/prettier': 'error', // 启用 Prettier 规则
    },
  },
  {
    ignores: [
      'node_modules/', // 忽略 node_modules
      'dist/', // 忽略构建产物
      'build/', // 忽略构建目录
      'public/', // 忽略静态资源
      '**/*.min.js', // 忽略压缩 JS
      '**/vendor/**', // 忽略第三方库
      'coverage/', // 忽略测试覆盖率报告
    ],
  },
];

配置.prettierrc

js 复制代码
{
  "printWidth": 120,
  "tabWidth": 2,
  "useTabs": false,
  "semi": true,
  "singleQuote": true,
  "quoteProps": "as-needed",
  "bracketSpacing": true,
  "bracketSameLine": true,
  "arrowParens": "avoid",
  "endOfLine": "lf",
  "jsxSingleQuote": false,
  "trailingComma": "es5",
  "embeddedLanguageFormatting": "auto"
}

各项属性的含义:

prettier属性 用途说明
embeddedLanguageFormatting 'auto':自动格式化嵌套代码(默认设置) const element = <div>{myFunction()}</div>; 'off':禁止嵌套代码的格式化 'on':始终格式化嵌套代码
endOfLine 'lf':使用 Linux/Mac 风格的行尾符(Line Feed,LF'crlf':使用 Windows 风格的行尾符(Carriage Return + Line Feed,CRLF'auto':根据操作系统自动选择合适的行尾符
arrowParens 'always':无论参数是一个还是多个,都强制添加括号。例如:(x) => x + 1 'avoid':如果只有一个参数,且没有箭头函数体括号,可以省略括号。例如:x => x + 1
jsxBracketSameLine false 表示 > 会放在新的一行,而 true 会将 > 放在行尾。这通常影响多行 JSX 元素的格式。
bracketSpacing true 表示在对象字面量的大括号 {} 内添加空格,如 { foo: bar }false 则不添加空格,如 {foo: bar}
trailingComma 'es5':在 ES5 中有效的地方添加尾逗号,比如对象和数组的最后一个元素。 'all':在所有可能的地方添加尾逗号(包括函数参数) 'none':不添加尾逗号
jsxSingleQuote false 表示在 JSX 属性值中使用双引号(默认行为),true 表示使用单引号
quoteProps 'as-needed':仅当属性名是 JavaScript 保留字或者包含空格等特殊字符时,才会加引号。 'consistent':如果对象中的某个属性加了引号,则其他所有属性也都会加引号。 'preserve':保持现有的引号使用。
singleQuote true 表示在字符串中使用单引号,而 false 则使用双引号
semi 是否在语句末尾添加分号
useTabs 是否使用制表符(tab)代替空格进行缩进
tabWidth 设置每个缩进级别的空格数
printWidth 指定代码的最大行宽。Prettier 会在达到指定宽度后自动换行,避免过长的行

配置 .prettierignore 有些文件夹及文件不需要格式化,要进行忽略

js 复制代码
dist
coverage
html
public
node_modules
*.yaml
pnpm-lock.yaml

在package.json中配置指令

js 复制代码
  "scripts": {
    "code:check": "eslint src --ext .ts,.tsx",
    "code:fix": "eslint src --ext .ts,.tsx --fix",
    "format:fix": "prettier --write ./",
    "format:check": "prettier --check ./",
  }

1.2 在VSCode中安装ESLint和Prettier

在扩展面板搜索ESLintPrettier - Code formatter

.vscode\extension.json文件内容

推荐扩展配置

json 复制代码
{
  "recommendations": [
    "esbenp.prettier-vscode",
    "dbaeumer.vscode-eslint",
    "streetsidesoftware.code-spell-checker",
    "foxundermoon.shell-format"
  ]
}

.vscode\settings.json文件内容

配置保存文件时运行ESLint和Prettier配置规则

json 复制代码
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit",
    "source.fixAll.stylelint": "explicit"
  },
}

1.3 报错处理

4. @typescript-eslint/typescript-estree与ypescript版本不匹配

WARNING: You are currently running a version of TypeScript which is not officially supported by @typescript-eslint/typescript-estree.

You may find that it works just fine, or you may not.

SUPPORTED TYPESCRIPT VERSIONS: >=3.3.1 <4.5.0

YOUR TYPESCRIPT VERSION: 4.9.5

Please only submit bug reports when using the officially supported version.

这个问题较严重,所以恢复到没修改之前的package.json和pnpm-lock.yaml版本,删除node_modules,重新安装依赖。

3. 找不到模块"@typescript-eslint/parser"或其相应的类型声明。

"d:/project/avatar-front-end-h5/node_modules/@typescript-eslint/parser/dist/index.d.ts"处有类型,但无法在当前 "moduleResolution" 设置下解析此结果。请考虑更新到 "node16"、"nodenext" 或 "bundler"。ts(2307) 这个错误通常是由于 TypeScript 的 moduleResolution 选项设置不正确,导致无法解析 @typescript-eslint/parser 模块。以下是几个可能的解决方案:

json 复制代码
{
  "compilerOptions": {
    "moduleResolution": "node16"
  }
}

2. Parsing error: ESLint was configured to run on <tsconfigRootDir>/eslint.config.ts using parserOptions.project: <tsconfigRootDir>/tsconfig.json

ESLint 在解析 eslint.config.ts 时遇到了问题,可能的原因是 parserOptions.project 指向的 tsconfig.json 配置不兼容。tsconfig.json的include 选项缺少 对eslint.config.ts的包含

js 复制代码
{
  "include": ["src", "eslint.config.ts"]
}

1. A config object has a "plugins" key defined as an array of strings

复制代码
Oops! Something went wrong! :(

ESLint: 9.23.0

A config object has a "plugins" key defined as an array of strings. It looks something like this:

json 复制代码
{
    "plugins": ["react-hooks"]
}

Flat config requires "plugins" to be an object, like this:

css 复制代码
{
    plugins: {
        react-hooks: pluginObject
    }
}

不是plugins部分本身配置的不是对象, 而是由于 react.configs.recommended配置不符合ESLint9的配置语法,注释掉

js 复制代码
{
   {
     plugins: {
      '@typescript-eslint': ts, // 使用已导入的插件对象
      react: react, // 使用已导入的插件对象
      'react-hooks': reactHooks, // 使用已导入的插件对象
      prettier: prettier, // 使用已导入的插件对象
    },
  },
  // react.configs.recommended, // React 推荐规则
}

二 扫描出的常见问题

2.1 告警类型

19. Arrow function has too many lines (52). Maximum allowed is 50 函数行数超过50行

18../components/Subway import should occur after import of @/utils/date-format 'import/order' 导入顺序不对,此条规则可关闭。

17.Unexpected use of 'location' 在 JavaScript 中,location 是浏览器环境中的一个全局变量。如果你在一个非浏览器的环境中(例如 Node.js)使用它,或者在代码中不小心使用了 location,ESLint 会认为不小心引用了全局的 location 对象。此条规则可关闭

js 复制代码
 await setQrUrl(location.href.includes('?isQrcode') ? location.href : location.href + '?isQrcode');

16.React Hook useMemo has missing dependencies: 'activeDay', 'id', 'locat.search', and 'navi'. Either include them or remove the dependency array. Mutable values like 'startDate.current' aren't valid dependencies because mutating them doesn't re-render the component react-hooks/exhaustive-deps

useMemo 钩子中,依赖项数组缺少了一些应该作为依赖项的变量。关闭此条规则

js 复制代码
// bad
const result = useMemo(() => {
  // 使用了 activeDay, id, locat.search 和 navi,但没有在依赖项数组中列出它们
  return someCalculation(activeDay, id, locat.search, navi);
}, []);  // 这里的依赖项数组为空,应该包含 activeDay, id, locat.search, navi
// good
const result = useMemo(() => {
  // 使用了 activeDay, id, locat.search 和 navi
  return someCalculation(activeDay, id, locat.search, navi);
}, [activeDay, id, locat.search, navi]);  // 将它们列为依赖项

15.React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked

useEffect 的依赖数组中使用了复杂的表达式,导致 React 无法静态分析这个依赖项是否会改变。

js 复制代码
// bad 
const MyComponent = () => {
  const [counter, setCounter] = useState(0);
  const [someArray, setSomeArray] = useState([1, 2, 3]);

  useEffect(() => {
    console.log('Effect triggered');
  }, [someArray.filter(item => item > 1)]);  // 复杂的表达式作为依赖项
};

// good
const MyComponent = () => {
  const [counter, setCounter] = useState(0);
  const [someArray, setSomeArray] = useState([1, 2, 3]);

  const filteredArray = someArray.filter(item => item > 1);  // 提取复杂表达式到一个变量

  useEffect(() => {
    console.log('Effect triggered');
  }, [filteredArray]);  // 使用提取后的变量作为依赖项
};

14. React Hook useEffect has a missing dependency: 'getPlaceData'. Either include it or remove the dependency array

React 的 useEffect Hook 会根据依赖数组中的变量来决定是否重新执行该副作用函数。如果副作用函数使用了某些外部变量(比如 getPlaceData),但是没有将这些变量添加到依赖数组中,React 无法确定这些变量的变化是否应该触发副作用的重新执行。关闭此条规则

js 复制代码
  const [places, setPlaces] = useState([]);

  const getPlaceData = () => {
    // 假设这是一个获取地点数据的函数
    fetch('/api/places')
      .then(response => response.json())
      .then(data => setPlaces(data));
  };
 // bad
  useEffect(() => {
    getPlaceData(); // 这个函数在 useEffect 中被调用
  }, []);  // 依赖数组为空,意味着 useEffect 只在组件挂载时执行一次
  
 // good
  useEffect(() => {
    getPlaceData(); 
  }, [getPlaceData]);  
  

13. warning Expected '===' and instead saw '==' eqeqeq, 要使用恒等表达式

12. Unexpected console statement 不允许使用console, 关闭该规则

11. '@/pages/chat' imported multiple times 重复导入

js 复制代码
import Chat from '@/pages/chat';
import Schedule from '@/pages/schedule';
import ChatPage from '@/pages/chat';

10. Expected an assignment or function call and instead saw an expression

ESLint 认为下面这两种写法是表达式但没有进行任何操作,尤其是在没有直接返回结果或赋值的情况下。 ,这是正常用法,关闭该规则。

js 复制代码
// 情况一
errorMsg && message.error(errorMsg);
// 情况二
type === 'train' ? (visible.value = true) : (deployVisible.value = true);

9.Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free @typescript-eslint/ban-ts-comment

设置@ts-ignore就是为了忽略下方一行,如果因此产生警告,不太合理。所以关闭这条规则。

8. 'selectOptions' is defined but never used. Allowed unused args must match /^_/u

未使用的变量警告,可以删除或将selectOptions改为_selectOptions, 这样eslint就不会检查了

js 复制代码
const cascaderchange = (value: ValueType, selectoptions: DefaultoptionType[]|DefaultoptionType[][])=>{
 emits('cascaderchange',value);
}

7. Prop 'value' requires default value to be set在 definePropsprops 里定义了 value未提供默认值, 关闭此条规则

js 复制代码
 // bad
 interface IProps {
    value?: Record<string, any>;
 }
 // good
  interface IProps {
    value?: Record<string, any> | undefined;
 }

6. Prop "upstep_val","content_class" is not in camelCase 变量应该使用小驼峰

5. 'v-html' directive can lead to XSS attack 不允许使用v-html 关闭此条规则

4. Forbidden non-null assertion @typescript-eslint/no-non-null-assertion

禁止使用 非空断言 (!),即在变量或表达式后加上 !,表示该值不可能是 nullundefined。 关闭规则

<a> 标签应该用于导航,而不是作为按钮使用 。如果 <a> 不是用于跳转,而是用于触发某些操作(如 onClick),应该改用 <button>。 关闭规则

2. warning img elements must have an alt prop, either with meaningful text, or an empty string for decorative images

所有 img 元素必须有 alt 属性 ,该属性应该包含描述图像内容的文本,如果图像只是装饰性用途,则 alt 属性应为空字符串 ""

1.Missing return type on function 需要在ts中指明函数返回类型

2.2 错误类型

10. Expected 'v-bind:key' directive to use the variables which are defined by the 'v-for' directive

v-bind:key 应该使用在 v-for 中定义的变量来确保每个元素的唯一性和高效的渲染

js 复制代码
// bad
<a-select-option v-for="opt in item.option" :key="item.value" :value="opt.value">{{opt.label}}</a-select-option>
// good
<a-select-option v-for="opt in item.option" :key="opt.value" :value="opt.value">{{opt.label}}</a-select-option>

9. The {} ("empty object") type allows any non-nullish value, including literals like 0 and "".

在 TypeScript 中,使用 {} 作为类型意味着 "任何非 nullundefined 的值",这包括所有原始类型(例如 0""false 等)。这意味着 {} 并不严格限制为对象类型。

js 复制代码
// bad
interface IProps {
  closeVisible: () => {};
}
// good
interface IProps {
  closeVisible: () => void;
}

8. 'url' is never reassigned. Use 'const' instead ,不会再被赋值的变量,应该用const声明

7. Component name "index" should always be multi-word vue文件的命名必须是多个单词,关闭掉

6. 第三方字体包src\assets\font\iconfont.js检测出问题 忽略对第三方字体文件的检查

5. 将生成到 CommonJS 输出的文件中不允许 'import.meta' 元属性。解决方法:

将 TypeScript 配置为生成 ES Module

js 复制代码
{
  "compilerOptions": {
    "module": "ESNext", // 或 "ESModule"
    "moduleResolution": "Node"
  }
}

4. Empty block statement 空的声明块

3.Unexpected any. Specify a different type ts 必须定义具体的类型,高频且改起来比较费劲的问题

2. xxx is defined but never used,'pushLastMessage' is assigned a value but never used 变量定义了未使用,高频错误

1. 'console','setTimeout','setInterval','clearInterval', 'NodeJS', 'React' is not defined

ESLint 认为 consolesetTimeoutsetInterval 等全局变量未定义。这通常是因为 ESLint 没有正确识别 JavaScript 运行环境(如浏览器或 Node.js)。

解决方法:

js 复制代码
pnpm add -D globals

在eslint.config.ts添加如下配置

ts 复制代码
import globals from 'globals';

export default [
  {
    languageOptions: {
      // ...
      globals: {
        ...globals.serviceworker,
        ...globals.browser,
        ...globals.node,
        ...globals.es2021,
      },
    },
  }
 ]
相关推荐
冴羽22 分钟前
SvelteKit 最新中文文档教程(17)—— 仅服务端模块和快照
前端·javascript·svelte
uhakadotcom23 分钟前
Langflow:打造AI应用的强大工具
前端·面试·github
前端小张同学32 分钟前
AI编程-cursor无限使用, 还有谁不会🎁🎁🎁??
前端·cursor
yanxy51236 分钟前
【TS学习】(15)分布式条件特性
前端·学习·typescript
uhakadotcom1 小时前
Caddy Web服务器初体验:简洁高效的现代选择
前端·面试·github
前端菜鸟来报道1 小时前
前端react 实现分段进度条
前端·javascript·react.js·进度条
花楸树2 小时前
前端搭建 MCP Client(Web版)+ Server + Agent 实践
前端·人工智能
wuaro2 小时前
RBAC权限控制具体实现
前端·javascript·vue
专业抄代码选手2 小时前
【JS】instanceof 和 typeof 的使用
前端·javascript·面试