背景:老项目没有配 eslint,或者 eslint 规则松散,配了跟没配一样。需要加上 eslint 或者加入更加严格的规则。
做减法
选择一个规则比较严格的扩展包,根据实际情况把不需要的规则一个个关掉,这个过程中一些歪瓜裂枣的写法无法遁形。个人喜欢用 eslint-config-airbnb
,常规配置如下:
module.exports = {root: true,globals: {page: true,REACT_APP_ENV: true,UMI_ENV: true,},settings: {react: {version: 'detect',},},parserOptions: {parser: '@typescript-eslint/parser',ecmaFeatures: {jsx: true,},},extends: ['airbnb','plugin:@typescript-eslint/recommended','prettier','plugin:react/recommended',],plugins: ['prettier', 'react', 'react-hooks', '@typescript-eslint'],rules: {'prettier/prettier': 'error','@typescript-eslint/no-explicit-any': 'error',eqeqeq: 'error','@typescript-eslint/no-unused-vars': ['error',{vars: 'local',args: 'none',varsIgnorePattern: 'usePresenter|^I',caughtErrors: 'none',},],},};module.exports = { root: true, globals: { page: true, REACT_APP_ENV: true, UMI_ENV: true, }, settings: { react: { version: 'detect', }, }, parserOptions: { parser: '@typescript-eslint/parser', ecmaFeatures: { jsx: true, }, }, extends: [ 'airbnb', 'plugin:@typescript-eslint/recommended', 'prettier', 'plugin:react/recommended', ], plugins: ['prettier', 'react', 'react-hooks', '@typescript-eslint'], rules: { 'prettier/prettier': 'error', '@typescript-eslint/no-explicit-any': 'error', eqeqeq: 'error', '@typescript-eslint/no-unused-vars': [ 'error', { vars: 'local', args: 'none', varsIgnorePattern: 'usePresenter|^I', caughtErrors: 'none', }, ], }, };module.exports = { root: true, globals: { page: true, REACT_APP_ENV: true, UMI_ENV: true, }, settings: { react: { version: 'detect', }, }, parserOptions: { parser: '@typescript-eslint/parser', ecmaFeatures: { jsx: true, }, }, extends: [ 'airbnb', 'plugin:@typescript-eslint/recommended', 'prettier', 'plugin:react/recommended', ], plugins: ['prettier', 'react', 'react-hooks', '@typescript-eslint'], rules: { 'prettier/prettier': 'error', '@typescript-eslint/no-explicit-any': 'error', eqeqeq: 'error', '@typescript-eslint/no-unused-vars': [ 'error', { vars: 'local', args: 'none', varsIgnorePattern: 'usePresenter|^I', caughtErrors: 'none', }, ], }, };
根据配置,全量代码 lint 一遍:
"lint": "eslint --ext .ts,.tsx,.js,.jsx --fix src/","lint": "eslint --ext .ts,.tsx,.js,.jsx --fix src/","lint": "eslint --ext .ts,.tsx,.js,.jsx --fix src/",
执行
yarn lintyarn lintyarn lint
你可能会得到这样的大礼包
根据实际情况把不需要的规则减去,我的配置如下:
module.exports = {root: true,globals: {page: true,REACT_APP_ENV: true,UMI_ENV: true,},settings: {react: {version: 'detect',},},parserOptions: {parser: '@typescript-eslint/parser',ecmaFeatures: {jsx: true,},},extends: ['airbnb','plugin:@typescript-eslint/recommended','prettier','plugin:react/recommended',],plugins: ['prettier', 'react', 'react-hooks', '@typescript-eslint'],rules: {'prettier/prettier': 'error','@typescript-eslint/no-explicit-any': 'error',eqeqeq: 'error','@typescript-eslint/no-unused-vars': ['error',{vars: 'local',args: 'none',varsIgnorePattern: 'usePresenter|^I',caughtErrors: 'none',},],'react-hooks/exhaustive-deps': 0,'@typescript-eslint/no-empty-function': 0,'@typescript-eslint/no-var-requires': 0,'react/react-in-jsx-scope': 0,'react/display-name': 0,'@typescript-eslint/no-non-null-assertion': 0,'react/jsx-filename-extension': 0,'react/function-component-definition': 0,'import/no-unresolved': 0,'import/extensions': 0,'arrow-body-style': 0,'react/jsx-no-useless-fragment': 0,'react/jsx-props-no-spreading': 0,'react/no-unstable-nested-components': 0,'jsx-a11y/alt-text': 0,'prefer-template': 0,'import/prefer-default-export': 0,'no-use-before-define': 0,'object-shorthand': 0,camelcase: 0,radix: 0,'class-methods-use-this': 0,'import/no-extraneous-dependencies': 0,'prefer-destructuring': 0,'jsx-a11y/click-events-have-key-events': 0,'jsx-a11y/no-static-element-interactions': 0,'consistent-return': 0,'no-unsafe-optional-chaining': 0,'jsx-a11y/media-has-caption': 0,'react/destructuring-assignment': 0,'no-plusplus': 0,'no-else-return': 0,'no-console': 0,'prefer-exponentiation-operator': 0,'func-names': 0,'react/require-default-props': 0,'no-return-assign': 0,'no-lonely-if': 0,'no-empty': 0,'jsx-a11y/anchor-is-valid': 0,'no-restricted-properties': 0,'operator-assignment': 0,'prefer-promise-reject-errors': 0,'no-prototype-builtins': 0,'prefer-object-spread': 0,'no-param-reassign': 0,'@typescript-eslint/no-empty-interface': 0,},};module.exports = { root: true, globals: { page: true, REACT_APP_ENV: true, UMI_ENV: true, }, settings: { react: { version: 'detect', }, }, parserOptions: { parser: '@typescript-eslint/parser', ecmaFeatures: { jsx: true, }, }, extends: [ 'airbnb', 'plugin:@typescript-eslint/recommended', 'prettier', 'plugin:react/recommended', ], plugins: ['prettier', 'react', 'react-hooks', '@typescript-eslint'], rules: { 'prettier/prettier': 'error', '@typescript-eslint/no-explicit-any': 'error', eqeqeq: 'error', '@typescript-eslint/no-unused-vars': [ 'error', { vars: 'local', args: 'none', varsIgnorePattern: 'usePresenter|^I', caughtErrors: 'none', }, ], 'react-hooks/exhaustive-deps': 0, '@typescript-eslint/no-empty-function': 0, '@typescript-eslint/no-var-requires': 0, 'react/react-in-jsx-scope': 0, 'react/display-name': 0, '@typescript-eslint/no-non-null-assertion': 0, 'react/jsx-filename-extension': 0, 'react/function-component-definition': 0, 'import/no-unresolved': 0, 'import/extensions': 0, 'arrow-body-style': 0, 'react/jsx-no-useless-fragment': 0, 'react/jsx-props-no-spreading': 0, 'react/no-unstable-nested-components': 0, 'jsx-a11y/alt-text': 0, 'prefer-template': 0, 'import/prefer-default-export': 0, 'no-use-before-define': 0, 'object-shorthand': 0, camelcase: 0, radix: 0, 'class-methods-use-this': 0, 'import/no-extraneous-dependencies': 0, 'prefer-destructuring': 0, 'jsx-a11y/click-events-have-key-events': 0, 'jsx-a11y/no-static-element-interactions': 0, 'consistent-return': 0, 'no-unsafe-optional-chaining': 0, 'jsx-a11y/media-has-caption': 0, 'react/destructuring-assignment': 0, 'no-plusplus': 0, 'no-else-return': 0, 'no-console': 0, 'prefer-exponentiation-operator': 0, 'func-names': 0, 'react/require-default-props': 0, 'no-return-assign': 0, 'no-lonely-if': 0, 'no-empty': 0, 'jsx-a11y/anchor-is-valid': 0, 'no-restricted-properties': 0, 'operator-assignment': 0, 'prefer-promise-reject-errors': 0, 'no-prototype-builtins': 0, 'prefer-object-spread': 0, 'no-param-reassign': 0, '@typescript-eslint/no-empty-interface': 0, }, };module.exports = { root: true, globals: { page: true, REACT_APP_ENV: true, UMI_ENV: true, }, settings: { react: { version: 'detect', }, }, parserOptions: { parser: '@typescript-eslint/parser', ecmaFeatures: { jsx: true, }, }, extends: [ 'airbnb', 'plugin:@typescript-eslint/recommended', 'prettier', 'plugin:react/recommended', ], plugins: ['prettier', 'react', 'react-hooks', '@typescript-eslint'], rules: { 'prettier/prettier': 'error', '@typescript-eslint/no-explicit-any': 'error', eqeqeq: 'error', '@typescript-eslint/no-unused-vars': [ 'error', { vars: 'local', args: 'none', varsIgnorePattern: 'usePresenter|^I', caughtErrors: 'none', }, ], 'react-hooks/exhaustive-deps': 0, '@typescript-eslint/no-empty-function': 0, '@typescript-eslint/no-var-requires': 0, 'react/react-in-jsx-scope': 0, 'react/display-name': 0, '@typescript-eslint/no-non-null-assertion': 0, 'react/jsx-filename-extension': 0, 'react/function-component-definition': 0, 'import/no-unresolved': 0, 'import/extensions': 0, 'arrow-body-style': 0, 'react/jsx-no-useless-fragment': 0, 'react/jsx-props-no-spreading': 0, 'react/no-unstable-nested-components': 0, 'jsx-a11y/alt-text': 0, 'prefer-template': 0, 'import/prefer-default-export': 0, 'no-use-before-define': 0, 'object-shorthand': 0, camelcase: 0, radix: 0, 'class-methods-use-this': 0, 'import/no-extraneous-dependencies': 0, 'prefer-destructuring': 0, 'jsx-a11y/click-events-have-key-events': 0, 'jsx-a11y/no-static-element-interactions': 0, 'consistent-return': 0, 'no-unsafe-optional-chaining': 0, 'jsx-a11y/media-has-caption': 0, 'react/destructuring-assignment': 0, 'no-plusplus': 0, 'no-else-return': 0, 'no-console': 0, 'prefer-exponentiation-operator': 0, 'func-names': 0, 'react/require-default-props': 0, 'no-return-assign': 0, 'no-lonely-if': 0, 'no-empty': 0, 'jsx-a11y/anchor-is-valid': 0, 'no-restricted-properties': 0, 'operator-assignment': 0, 'prefer-promise-reject-errors': 0, 'no-prototype-builtins': 0, 'prefer-object-spread': 0, 'no-param-reassign': 0, '@typescript-eslint/no-empty-interface': 0, }, };
处理老代码
按上面配置执行 lint 时,新加的代码是能通过的。但是原来的代码可能还是一大堆问题
原来的代码如果不再改动,那么我们完全不需要去管了,但这是不可能的。一旦有迭代需要改原来的代码,哪怕只改了一行代码,提交代码的时候 git 钩子是过不去的,而且打开这些代码时,编辑器估计是一片红。
秉着别人拉的shi,凭什么要我去扒的原则,我们不想去改迭代需求之外的代码,但是又要能正常提交。需要借助 eslint 的一个配置,那就是 overrides
。
overrides
允许给特定的文件配置特定的rules
,比如:
overrides: [{files: ['src/pages/activities/list.tsx',],rules: {'@typescript-eslint/no-explicit-any': 'off',eqeqeq: 'off',},},],overrides: [ { files: [ 'src/pages/activities/list.tsx', ], rules: { '@typescript-eslint/no-explicit-any': 'off', eqeqeq: 'off', }, }, ],overrides: [ { files: [ 'src/pages/activities/list.tsx', ], rules: { '@typescript-eslint/no-explicit-any': 'off', eqeqeq: 'off', }, }, ],
需要将所有的旧代码文件配置到 files
数组里,这可是个体力活,写个脚本去处理:
import glob from 'glob';import path from 'path';import fs from 'fs-extra';export const getFiles = () =>new Promise<string[]>((resolve, reject) => {glob('**',{cwd: path.join('H:/你的项目地址/src'),ignore: [],nodir: true,dot: true,},(err, files) => {if (err) {reject(err);return;}resolve(files);},);});getFiles().then((files) => {fs.writeFileSync('./overrides.txt',files.filter((s) =>s.includes('.vue') ||s.includes('.ts') ||s.includes('.js') ||s.includes('.tsx'),).map((s) => `'src/${s}',`).join('\r'),);});import glob from 'glob'; import path from 'path'; import fs from 'fs-extra'; export const getFiles = () => new Promise<string[]>((resolve, reject) => { glob( '**', { cwd: path.join('H:/你的项目地址/src'), ignore: [], nodir: true, dot: true, }, (err, files) => { if (err) { reject(err); return; } resolve(files); }, ); }); getFiles().then((files) => { fs.writeFileSync( './overrides.txt', files .filter( (s) => s.includes('.vue') || s.includes('.ts') || s.includes('.js') || s.includes('.tsx'), ) .map((s) => `'src/${s}',`) .join('\r'), ); });import glob from 'glob'; import path from 'path'; import fs from 'fs-extra'; export const getFiles = () => new Promise<string[]>((resolve, reject) => { glob( '**', { cwd: path.join('H:/你的项目地址/src'), ignore: [], nodir: true, dot: true, }, (err, files) => { if (err) { reject(err); return; } resolve(files); }, ); }); getFiles().then((files) => { fs.writeFileSync( './overrides.txt', files .filter( (s) => s.includes('.vue') || s.includes('.ts') || s.includes('.js') || s.includes('.tsx'), ) .map((s) => `'src/${s}',`) .join('\r'), ); });
把文件 overrides.txt
里的内容复制到 files
数组中。内容可能会非常的多,可以将 overrides
配置在独单的文件里,比如:
./ignoreCheckAndLintFiles/.eslintrc.js
配置到这个文件里
// files只允许删减,不允许增加module.exports = {overrides: [{files: ['src/pages/activities/list.tsx',// ...省略],rules: {'@typescript-eslint/no-explicit-any': 'off',eqeqeq: 'off',// ... 省略},},],};// files只允许删减,不允许增加 module.exports = { overrides: [ { files: [ 'src/pages/activities/list.tsx', // ...省略 ], rules: { '@typescript-eslint/no-explicit-any': 'off', eqeqeq: 'off', // ... 省略 }, }, ], };// files只允许删减,不允许增加 module.exports = { overrides: [ { files: [ 'src/pages/activities/list.tsx', // ...省略 ], rules: { '@typescript-eslint/no-explicit-any': 'off', eqeqeq: 'off', // ... 省略 }, }, ], };
在项目根目录的 .eslintrc.js
文件中引入
const overrides = require('./ignoreCheckAndLintFiles/.eslintrc.js');module.exports = {// ... 省略overrides: overrides.overrides,};// 可以添加规则 删除、忽略规则先说明原因 请严格执行const overrides = require('./ignoreCheckAndLintFiles/.eslintrc.js'); module.exports = { // ... 省略 overrides: overrides.overrides, }; // 可以添加规则 删除、忽略规则先说明原因 请严格执行const overrides = require('./ignoreCheckAndLintFiles/.eslintrc.js'); module.exports = { // ... 省略 overrides: overrides.overrides, }; // 可以添加规则 删除、忽略规则先说明原因 请严格执行
再执行lint
,强迫症都治好了
ts check 配置
配置 tsconfig.json 文件,禁止使用 any
,严格模式
"noImplicitAny": true,"strict": true"noImplicitAny": true, "strict": true"noImplicitAny": true, "strict": true
配置 ts ckeck 命令
"tsc": "tsc --noEmit --skipLibCheck""tsc": "tsc --noEmit --skipLibCheck""tsc": "tsc --noEmit --skipLibCheck"
老代码肯定是过不去的,配置 exclude
数组,把旧代码文件放进去,也是配置在单独的文件里。
./ignoreCheckAndLintFiles/tsconfig.json
{"extends": "../tsconfig.json","exclude": ["../src/pages/login.tsx"// ... 省略]}{ "extends": "../tsconfig.json", "exclude": [ "../src/pages/login.tsx" // ... 省略 ] }{ "extends": "../tsconfig.json", "exclude": [ "../src/pages/login.tsx" // ... 省略 ] }
也是用脚本去处理
import glob from 'glob';import path from 'path';import fs from 'fs-extra';export const getFiles = () =>new Promise<string[]>((resolve, reject) => {glob('**',{cwd: path.join('H:/VankeService/katarina/src'),ignore: [],nodir: true,dot: true,},(err, files) => {if (err) {reject(err);return;}resolve(files);},);});getFiles().then((files) => {fs.writeJSONSync('./exclude.json', {extends: '../tsconfig.json',exclude: files.filter((s) => s.includes('.vue') || s.includes('.ts') || s.includes('.tsx'),).map((s) => `../src/${s}`),});});import glob from 'glob'; import path from 'path'; import fs from 'fs-extra'; export const getFiles = () => new Promise<string[]>((resolve, reject) => { glob( '**', { cwd: path.join('H:/VankeService/katarina/src'), ignore: [], nodir: true, dot: true, }, (err, files) => { if (err) { reject(err); return; } resolve(files); }, ); }); getFiles().then((files) => { fs.writeJSONSync('./exclude.json', { extends: '../tsconfig.json', exclude: files .filter( (s) => s.includes('.vue') || s.includes('.ts') || s.includes('.tsx'), ) .map((s) => `../src/${s}`), }); });import glob from 'glob'; import path from 'path'; import fs from 'fs-extra'; export const getFiles = () => new Promise<string[]>((resolve, reject) => { glob( '**', { cwd: path.join('H:/VankeService/katarina/src'), ignore: [], nodir: true, dot: true, }, (err, files) => { if (err) { reject(err); return; } resolve(files); }, ); }); getFiles().then((files) => { fs.writeJSONSync('./exclude.json', { extends: '../tsconfig.json', exclude: files .filter( (s) => s.includes('.vue') || s.includes('.ts') || s.includes('.tsx'), ) .map((s) => `../src/${s}`), }); });
把 exclude.json
内容复制到 ./ignoreCheckAndLintFiles/tsconfig.json
修改 ts check 命令
"tsc": "tsc --project ./ignoreCheckAndLintFiles/tsconfig.json --noEmit --skipLibCheck""tsc": "tsc --project ./ignoreCheckAndLintFiles/tsconfig.json --noEmit --skipLibCheck""tsc": "tsc --project ./ignoreCheckAndLintFiles/tsconfig.json --noEmit --skipLibCheck"
新代码里引入旧代码还是会报错,所以尽量不要再去使用旧代码,或者旧代码里使用
// @ts-ignore