如何修复ESLint无法唯一确定@typescript-eslint插件的错误?
问题背景
在Visual Studio 2022社区版17.6.3中,基于“ASP.NET Core with React.js”模板创建的项目(ClientApp目录由create-react-app生成),启用ESLint代码检查时,始终遇到以下错误:
yarn run v1.22.19
$ eslint ./src/Oops! Something went wrong! :(
ESLint: 8.48.0
ESLint couldn't determine the plugin "@typescript-eslint" uniquely.
- C:\path\to\MyProject\ClientApp\node_modules@typescript-eslint\eslint-plugin\dist\index.js (loaded in "src.eslintrc.json")
- C:\path\to\MyProject\ClientApp\node_modules\eslint-config-react-app\node_modules@typescript-eslint\eslint-plugin\dist\index.js (loaded in "src.eslintrc.json » eslint-config-react-app#overrides[0]")
Please remove the "plugins" setting from either config or remove either plugin installation.
推测错误源于项目不同依赖路径下存在多个版本的@typescript-eslint,但对错误信息中“src.eslintrc.json » eslint-config-react-app#overrides[0]”的含义感到困惑——自身的.eslintrc.json中并没有相关配置。
已尝试以下方案但未解决:
- 使用与文件系统一致的大小写路径访问项目
- 手动保存package.json修改
- 执行
dedupe命令 - 删除node_modules、package-lock.json/yarn.lock后重新安装依赖
- 分别用npm和yarn尝试上述操作,切换时删除对应锁文件
项目配置文件
package.json
{ "name": "myapp", "version": "0.1.0", "private": true, "dependencies": { "bootstrap": "^5.3.1", "http-proxy-middleware": "^2.0.6", "jquery": "^3.7.0", "merge": "^2.1.1", "oidc-client": "^1.11.5", "react": "^18.2.0", "react-bootstrap": "^2.8.0", "react-dom": "^18.2.0", "react-in-viewport": "^1.0.0-alpha.30", "react-router-bootstrap": "^0.26.2", "react-router-dom": "^6.15.0", "reactstrap": "^9.2.0", "rimraf": "^5.0.1", "web-vitals": "^3.4.0" }, "devDependencies": { "@babel/eslint-parser": "^7.22.11", "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@types/jest": "^29.5.4", "@types/react": "^18.2.21", "@typescript-eslint/eslint-plugin": "^6.4.1", "@typescript-eslint/parser": "^6.4.1", "ajv": "^8.12.0", "cross-env": "^7.0.3", "eslint": "^8.48.0", "eslint-plugin-flowtype": "^8.0.3", "eslint-plugin-import": "^2.28.1", "eslint-plugin-jest": "^27.2.3", "eslint-plugin-jest-formatting": "^3.1.0", "eslint-plugin-jsdoc": "^46.5.0", "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-react": "^7.33.2", "nan": "^2.17.0", "react-scripts": "^5.0.1", "typescript": "4.3.5" }, "jest": { "coveragePathIgnorePatterns": [ "/src/index.js", "/src/reportWebVitals.js", "/src/service-worker.js", "/src/serviceWorkerRegistration.js", "/src/setupProxy.js", "/src/components/", "/src/Models" ], "coverageReporters": [ "json", "html", "text" ] }, "overrides": { "autoprefixer": "10.4.5", "semver": "^7.5.2", "svgo": "^2.0.0" }, "resolutions": { "css-what": "^5.0.1", "nth-check": "^2.0.0" }, "scripts": { "prestart": "node aspnetcore-https && node aspnetcore-react", "start": "rimraf ./build && react-scripts start", "build": "react-scripts build", "test": "cross-env CI=true react-scripts test --env=jsdom", "watch": "cross-env CI=true react-scripts test --env=jsdom --watchAll --coverage", "eject": "react-scripts eject", "lint": "eslint ./src/" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }, "peerDependencies": { "@babel/core": "^7.11.0", "@babel/plugin-syntax-flow": "^7.14.5", "@babel/plugin-transform-react-jsx": "^7.14.9", "@popperjs/core": "^2.11.8" } }
.eslintrc.json
{ "parser": "@typescript-eslint/parser", "extends": [ "eslint:recommended", "plugin:import/errors", "plugin:import/warnings", "plugin:react/recommended", "plugin:jsdoc/recommended", "react-app" ], "env": { "browser": true, "node": true, "mocha": true, "es6": true, "jest": true }, "overrides": [ { "files": [ "**/*.test.js" ], "env": { "jest": true }, "plugins": [ "jest", "jest-formatting" ], "rules": { // Omitted for brevity, I can edit them back in if needed } } ], "plugins": [ "react", "import", "jsx-a11y", "jest", "jsdoc", "@typescript-eslint" ], "parserOptions": { "ecmaVersion": 6, "sourceType": "module", "ecmaFeatures": { "jsx": true } }, "rules": { // Omitted for brevity, I can edit them back in if needed }, "settings": { "react": { "version": "18" }, "import/resolver": { "node": { "extensions": [ ".js", ".jsx", ".ts", ".tsx" ] } } } }
tsconfig.json
{ "compilerOptions": { "target": "ES6", "lib": ["es6", "DOM"], "jsx": "react", "module": "commonjs", "moduleResolution": "node", "types": ["jest", "node", "@types/jest"], "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "noImplicitAny": true, "strictNullChecks": true, "skipDefaultLibCheck": true, "skipLibCheck": true } }
解决方案
核心原因
错误信息里的“src.eslintrc.json » eslint-config-react-app#overrides[0]”意思是:你自己的.eslintrc.json继承了react-app配置,而react-app内部的overrides[0]配置也加载了@typescript-eslint插件,加上你自己在plugins数组里手动声明了@typescript-eslint,导致ESLint加载了两个不同路径下的插件实例(一个是你直接安装的顶级node_modules,一个是eslint-config-react-app依赖的嵌套node_modules)。
修复步骤
从你的.eslintrc.json中移除@typescript-eslint插件声明
打开.eslintrc.json,找到plugins数组,删除其中的"@typescript-eslint"项。因为react-app配置已经包含了该插件的加载,不需要重复声明。修改后的plugins数组应该是:"plugins": [ "react", "import", "jsx-a11y", "jest", "jsdoc" ]锁定@typescript-eslint版本与react-app依赖的版本一致(可选但推荐)
先执行yarn list @typescript-eslint/eslint-plugin查看eslint-config-react-app依赖的嵌套版本,然后在package.json的resolutions(yarn)或overrides(npm)中添加配置强制统一版本:"resolutions": { "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "css-what": "^5.0.1", "nth-check": "^2.0.0" }重新安装依赖
删除node_modules和yarn.lock(或package-lock.json),然后执行yarn install(或npm install)。验证修复
运行yarn lint,错误应该消失。
补充说明
- 不需要手动声明
@typescript-eslint插件的原因是,react-app配置已经集成了TypeScript相关的ESLint配置,包括插件和解析器的加载。 - 如果后续需要自定义@typescript-eslint的规则,直接在
.eslintrc.json的rules数组中添加即可,无需重复声明插件。
内容的提问来源于stack exchange,提问作者sbridewell

