React 项目ts与js 之eslint 代码检查

5,135 阅读5分钟

什么是代码检查

代码检查主要是用来发现代码错误、统一代码风格。

在 JavaScript 项目中,我们一般使用ESLint来进行代码检查。它通过插件化的特性极大的丰富了适用范围,搭配typescript-eslint-parser之后,甚至可以用来检查 TypeScript 代码。

TSLint 与 ESLint 类似,不过除了能检查常规的 js 代码风格之外,TSLint 还能够通过 TypeScript 的语法解析,利用类型系统做一些 ESLint 做不到的检查。

由于性能问题,TypeScript 官方决定全面采用 ESLint,甚至把仓库(Repository)作为测试平台,而 ESLint 的 TypeScript 解析器也成为独立项目,专注解决双方兼容性问题。

JavaScript 代码检验工具 ESLint 在 TypeScript 团队发布全面采用 ESLint 之后,发布 typescript-eslint 项目,以集中解决 TypeScript 和 ESLint 兼容性问题。而 ESLint 团队将不再维护 typescript-eslint-parser,也不会在 Npm 上发布,TypeScript 解析器转移至Github 的 typescript-eslint/parser

所以本篇文章主要围绕 typescript-eslint/parser 进行ts 与js 的并存检查

安装依赖

安装eslint

  1. 安装eslint 到本地

    cnpm i eslint -D

  2. 初始化配置文件(推荐尝试下这个命令,而不是直接拿别人写好的.eslintrc.xx文件)

    ./node_modules/.bin/eslint --init

  3. ./node_modules/.bin/eslint -h查看运行指令,运行想要检查的指令

    $ ./node_modules/.bin/eslint yourfile.js

安装 @typescript-eslint/parser 解析器

eslint 可以解析typescript 语法

cnpm i @typescript-eslint/parser -D

安装推荐插件

  1. 辅助解析.ts 文件

    npm i @typescript-eslint/eslint-plugin -D

  2. 安装格式化插件

    使lint 工具专注于质量检查,同时避免lint 定义的格式语法与集成的格式化插件冲突

    npm i prettier eslint-config-prettier -D

    eslint-config-prettier是一个禁用与Prettier冲突的规则的配置,确保将它放在extends数组的最后,这样它就有机会覆盖其他配置

  3. 使用Airbnb

    Airbnb有两个配置,一个是基础的eslint-config-airbnb-base,另一个包含React规则的eslint-config-airbnb

    npm info "eslint-config-airbnb@latest" peerDependencies 查看 peerDependencies,并安装相应的包

使用方法

在eslint.js 配置文件中 ,针对上述插件的添加配置文件

module.exports = {
    // 预定义全局变量
    env: {
        browser: true,
        commonjs: true,
        node: true,
        es6: true
    },
    parser: '@typescript-eslint/parser',
    plugins: ['react', '@typescript-eslint'],
    parserOptions: {
        project: './tsconfig.json',
        ecmaFeatures: {
            jsx: true
        }
    },
    settings: {
        'import/resolver': {
            webpack: {
                config: './webpack/webpack.dev.config.js'
            }
        }
    },
    globals: {
        window: true
    },
    root: true, //项目根目录,停止向上搜索配置文件
    extends: [
        'eslint:recommended',
        'airbnb',
        'plugin:@typescript-eslint/recommended',
        'plugin:@typescript-eslint/eslint-recommended',
        'prettier', // 禁用与Prettier 冲突的规则
        'prettier/@typescript-eslint', // 针对添加的插件 弃用对应的格式化规则
        'prettier/react' //eslint-config-airbnb 启用 eslint-plugin-react 规则,故在prettier 修改
    ],
    rules: {
        'array-bracket-spacing': ['error'],
        'arrow-body-style': ['error', 'as-needed'],
        'arrow-spacing': ['error'],
        'block-spacing': ['error'],
        'brace-style': ['error'],
        camelcase: ['warn'],
        'capitalized-comments': ['off'],
        'comma-dangle': [
            'error',
            {
                arrays: 'never',
                objects: 'never',
                imports: 'never',
                exports: 'never',
                functions: 'never'
            }
        ],
        'comma-spacing': ['error'],
        'comma-style': ['error'],
        'computed-property-spacing': ['error'],
        curly: ['off'],
        'dot-location': ['error', 'property'],
        'dot-notation': ['off'],
        'eol-last': ['off'],
        'func-call-spacing': ['error'],
        'func-name-matching': ['error'],
        'generator-star-spacing': ['error'],
        indent: [
            'error',
            4,
            {
                SwitchCase: 1
            }
        ],
        'jsx-quotes': ['error', 'prefer-single'],
        'key-spacing': ['error'],
        'keyword-spacing': ['error'],
        'linebreak-style': ['error'],
        'lines-around-comment': [
            'off',
            {
                beforeLineComment: true
            }
        ],
        'lines-around-directive': ['error'],
        'new-cap': ['off'],
        'newline-after-var': ['off'],
        'newline-before-return': ['off'],
        'new-parens': ['error'],
        'no-cond-assign': ['off'],
        'no-console': ['off'],
        'no-delete-var': ['off'],
        'no-extra-bind': ['error'],
        'no-extra-parens': [
            'error',
            'all',
            {
                returnAssign: false,
                ignoreJSX: 'all',
                enforceForArrowConditionals: false,
                nestedBinaryExpressions: false
            }
        ],
        'no-floating-decimal': ['error'],
        'no-lonely-if': ['error'],
        'no-multiple-empty-lines': [
            'error',
            {
                max: 1,
                maxBOF: 0,
                maxEOF: 0
            }
        ],
        'no-multi-spaces': ['error'],
        'no-redeclare': ['error'],
        'no-undef-init': ['error'],
        'no-useless-computed-key': ['error'],
        'no-useless-rename': ['error'],
        'no-useless-return': ['error'],
        'no-var': ['warn'],
        'no-whitespace-before-property': ['error'],
        'object-curly-newline': ['off'],
        'object-curly-spacing': ['off'],
        'object-shorthand': ['off'],
        'operator-assignment': ['warn'],
        'one-var': ['off'],
        'one-var-declaration-per-line': ['error', 'initializations'],
        'padded-blocks': ['off', 'never'],
        'prefer-arrow-callback': ['off'],
        'prefer-spread': ['off'],
        'prefer-template': ['off'],
        quotes: ['error', 'single'],
        'quote-props': ['off', 'as-needed'],
        'react/prop-types': ['off'],
        'react/jsx-curly-spacing': ['error'],
        'react/jsx-space-before-closing': ['off', 'never'],
        'rest-spread-spacing': ['error'],
        semi: ['off', 'never'],
        'space-before-blocks': ['error'],
        'space-before-function-paren': ['off', 'never'],
        'space-in-parens': ['error', 'never'],
        'space-infix-ops': ['error'],
        'space-unary-ops': [
            'error',
            {
                words: true,
                nonwords: false
            }
        ],
        'template-curly-spacing': ['error'],
        'yield-star-spacing': ['error'],
        yoda: ['error'],
        eqeqeq: ['off'],
        'no-eval': ['off'],
        'no-template-curly-in-string': ['off'],
        'no-shadow': ['off'],
        'no-return-assign': ['off'],
        'global-require': ['off'],
        'no-underscore-dangle': ['off'],
        'func-names': ['off'],
        'prefer-destructuring': ['off'],
        'prefer-const': ['error'], // let与const 优先级
        indent: ['off'],
        'no-restricted-syntax': ['off'],
        'guard-for-in': ['off'],
        'no-continue': ['off'],
        'no-extend-native': ['off'],
        'no-param-reassign': ['off'],
        'consistent-return': ['off'],
        radix: ['off'],
        'no-plusplus': ['off'],
        'no-unused-expressions': ['off'],
        'no-script-url': ['off'],
        'no-restricted-globals': ['off'],
        'import/no-cycle': ['off'],
        'jsx-a11y/alt-text': ['off'],
        'react/sort-comp': ['off'],
        'react/destructuring-assignment': ['off'],
        'import/no-webpack-loader-syntax': ['off'],
        'react/jsx-indent': ['off'],
        'react/jsx-indent-props': ['off'],
        'react/jsx-filename-extension': ['error', { extensions: ['.ts', '.tsx', '.js', '.jsx'] }],
        'react/jsx-one-expression-per-line': ['off'],
        '@typescript-eslint/explicit-member-accessibility': ['off'],
        '@typescript-eslint/interface-name-prefix': ['error', 'always'],
        'react/no-access-state-in-setstate': ['off'],
        'react/no-array-index-key': ['off'],
        'jsx-a11y/click-events-have-key-events': ['off'],
        'jsx-a11y/no-static-element-interactions': ['off'],
        'jsx-a11y/anchor-is-valid': ['off'],
        'react/jsx-wrap-multilines': ['off'],
        '@typescript-eslint/explicit-function-return-type': [
            'off',
            {
                allowExpressions: true,
                allowTypedFunctionExpressions: true
            }
        ],
        'jsx-a11y/label-has-for': ['off'],
        'jsx-a11y/label-has-associated-control': ['off'],
        'react/jsx-boolean-value': ['off'],
        // 'lines-between-class-member':['error'], // 默认打开,类成员有空格
        '@typescript-eslint/no-var-requires': ['off'],
        '@typescript-eslint/no-explicit-any': ['off'],
        '@typescript-eslint/no-use-before-define': ['error', { functions: false }],
        'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
        'import/order': ['off'],
        'import/newline-after-import': ['off'],
        'import/no-unresolved': [2, { commonjs: true, amd: true }]
    }
};

运行eslint命令

在package.json 添加可执行命令

eslint -c ./.eslintrc.js --ext .js,.jsx,.ts,.tsx ./

eslint 默认检查js 文件,需要添加.ts 文件说明,--fix 默认修复选项,有时会导致自动修复错误,故不添加

vscode 中集成 eslint 检查typescript

默认情况下,ESLint扩展仅在javascript和javascriptreact文件上运行。要告诉它在TS文件上运行,您需要将eslint.validate设置更新为:

    "eslint.validate": [
      "javascript",
      "javascriptreact",
      "typescript",
      "typescriptreact"
    ]

参考文档

  1. TypeScript 官方决定全面采用 ESLint

  2. 使用 TSLint 或使用 ESLint + typescript-eslint-parser

  3. 最全的Eslint配置模板,从此统一团队的编程习惯