preface

I worked as an intern in a small company for some time, and the code format and standardization of the project I took over could not be described. In order to ensure the maintainability of the project, I decided to plug in Typescript, while Eslint and Prettier were used to normalize the code, becoming the next CodeReview.

Typescript is a strongly typed language, and there are a lot of pain points at first, but after a while you start to enjoy the benefits of Typescript: fewer bugs, easier to read, and maintainable code.

Common code formatting tools include ESlint, TSLint, StandardJS. TS has officially decided to abandon TSLint and embrace ESLint. ESlint will therefore be used for technology selection.

The main features of Eslint include validation of code format, validation of code quality, JS specifications such as using === instead of == to determine equality, and naming variables with humps instead of underscores. Prettier, where code is formatted, doesn’t check quality (single line length, TAB length, Spaces, comma expressions)

Installation-related dependencies

1.1 installation Typescript

A global installation is recommended, and you can use TS in other projects.

npm install -g typescript
Copy the code

1.2 Installation Statement File

React, react-DOM declaration files, and ts-loader to load TS

npm install --save-dev @types/react @types/react-dom ts-loader
Copy the code

1.3 configuration tsconfig. Json

When using Typescript, you need to configure related rules based on actual project requirements. The configuration varies depending on the project. For details, see the TS official website. My configuration items are as follows:

{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true."noUnusedParameters": true."outDir": "build/dist"."baseUrl": "."."strict": true."noImplicitAny": true."removeComments": true."preserveConstEnums": true."sourceMap": true."forceConsistentCasingInFileNames": true."strictPropertyInitialization": true."experimentalDecorators": true."noImplicitReturns": true."moduleResolution": "node"."strictNullChecks": true."esModuleInterop": true."noUnusedLocals": true."importHelpers": true."noImplicitThis": false."suppressImplicitAnyIndexErrors": false."skipLibCheck": true."noResolve": false."module": "es2015"."allowJs": true."target": "es5"."jsx": "react"."lib": [
      "es5"."es2015"."dom"."es7"."es2018"]."paths": {
      "@ / *": [
        "./src/*"]}},"exclude": [
    "node_modules"."build"."scripts"."acceptance-tests"."webpack"."jest"."src/setupTests.ts"."tslint:latest"."tslint-config-prettier"]}Copy the code

ESLint+Prettier

There are two main options for migrating JS projects to TS projects: ESLint and TSLint. TSLint is only for TS code, so if TSLint is used to regulate TS code, JS code needs to use other tools. ESLint can regulate not only JS code, but also TS code by configuring parsers. In addition, TypeScript officially adopted ESLint for performance reasons. Therefore, ESLint and Prettier are used in this project to normalize TS and JS code.

2.1 ESLint specifies TS code

2.1.1 Installing ESLint dependencies

npm i eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin --save-dev
Copy the code
  • Eslint: The core code for ESLint

  • @typescript-eslint/parser: A parser for ESLint that parses typescript files to check and standardize typescript code

  • @typescript-eslint/eslint-plugin: This is an ESLint plugin that contains various specifications for checking typescript code

Once the dependencies are installed, they need to be configured in.eslintrc.js in the project root directory, including parsers, inherited code specifications, plug-ins, and environments:

module.exports = {
   parser:  '@typescript-eslint/parser'.// Define a parser for ESLint
   extends: ['plugin:@typescript-eslint/recommended'].// Define a subspecification for file inheritance
   plugins: ['@typescript-eslint'].// Defines the plugins that the ESLint file depends on
   env:{                          // Specify the environment in which the code is run
       browser: true.node: true,}}Copy the code

2.2 Configuring the React code

The project is based on AntDesignPro that relies on React, so you need to install an Eslint plug-in that regulates React code.

2.2.1 Installing plug-ins

npm i eslint-plugin-react --save-dev
Copy the code

2.2.2 Configuring the React Specification

Then modify the.eslintrc.js configuration as follows:

module.exports = {
   parser:  '@typescript-eslint/parser'.extends: [
    'plugin:react/recommended'.'plugin:@typescript-eslint/recommended'].// Use the recommended React code to test the specification
   plugins: ['@typescript-eslint'].env: {browser: true.node: true,},settings: {// Automatically discover the React version to regulate the React code
       "react": {
           "pragma": "React"."version": "detect"}},parserOptions: {// Specifies that ESLint can parse JSX syntax
       "ecmaVersion": 2019."sourceType": 'module'."ecmaFeatures": {jsx:true
       }
   }
   rules: {
   // In Rules you can customize your React code specification.}}Copy the code

2.3 access Prettier

2.3.1 Installing Dependencies

npm install prettier eslint-config-prettier eslint-plugin-prettier -g
Copy the code
  • Prettier: The core code for the prettier plug-in

  • Eslint-config-prettier: Resolves the conflict between the style specification in ESLint and the style specification in Prettier by taking the style specification of Prettier as the standard

  • Eslint-plugin-prettier: The use of prettier as the ESLint specification

2.3.2 Configuring rules

Create the. Prettierrc. js file in the root directory of the project and configure the prettier code check rule

module.exports = {
    // Contains a maximum of 80 characters
    printWidth: 80.// End-of-line semicolon
    semi: false./ / single quotation marks
    singleQuote: true.// Use trailing commas whenever possible (including function arguments)
    trailingComma: 'all'.// Prints Spaces between parentheses in the object text.
    bracketSpacing: true.// > The tag is placed at the end of the last line, not on its own on the next line
    jsxBracketSameLine: false.// Arrow parentheses
    arrowParens: 'avoid'.// Insert a special @format tag at the top of the file to specify that the file format needs to be formatted.
    insertPragma: false./ / the indentation
    tabWidth: 4.// Use TAB or space
    useTabs: false,}Copy the code

2.3.3 Configuring Eslint with Prettier

Modify the.eslintrc.js file where prettier is added

module.exports = {
    parser: '@typescript-eslint/parser'.extends: [
        'plugin:react/recommended'.'plugin:@typescript-eslint/recommended'.'prettier/@typescript-eslint'.'plugin:prettier/recommended',].plugins: ['@typescript-eslint'.'react'].env: {
        browser: true.node: true.es6: true,},rules: {
        quotes: ['error'.'single'].// Enforces single quotes
        semi: ['error'.'never'].// Require or disallow semicolons instead of ASI
        camelcase: 0.// Bactrian camel naming format
        eqeqeq: 2.// Must use congruence
        yoda: [2.'never'].// Disable yoda conditions
        strict: [2.'never'].// Disable strict mode, forbid 'use strict' anywhere
        'no-extra-boolean-cast': 2.// Disallow unnecessary bool conversions
        'no-lone-blocks': 2.// Disallow unnecessary nesting blocks
        'no-plusplus': 0.// Disallow ++, --
        'no-proto': 2.// Disallow the __proto__ attribute
        'no-self-compare': 2.// Cannot compare itself
        'no-undef': 2.// There cannot be undefined variables
        'no-unreachable': 2.// There can be no code that cannot be executed
        'no-unused-expressions': 2.// Disallow useless expressions
        'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0.'no-alert': 2.// Disable alert
        'no-caller': 1.// Disallow arguments. Caller or arguments.callee
        'no-inline-comments': 2.// Forbid line remarks
        'no-func-assign': 2.// Disallow duplicate function declarations
        'no-eval': 2.// Disallow eval,
        'no-empty': 2.// Block statements cannot be empty
        'no-const-assign': 2.// Disallow modifying variables declared by const
        'no-var': 2.// Do not use var
        'no-multiple-empty-lines': [1, { max: 2}].// No more than 2 blank lines
        'no-extra-semi': 'error'.// Disallow unnecessary semicolons
        'array-bracket-spacing': [2.'never'].// Allow extra Spaces in non-empty arrays
        'linebreak-style': ['error'.'unix'].// Enforce consistent line breaks
        'brace-style': [2.'1tbs', { allowSingleLine: true}].// if while function {must be on the same line as if, Java style.
        'comma-dangle': 0.// Array and object key-value pairs last comma, never argument: cannot have trailing comma, always argument: must have trailing comma,
        'comma-spacing': [2, { before: false.after: true}].// Control the Spaces before and after commas
        'computed-property-spacing': [2.'never'].// Whether Spaces are required before [and] when using square brackets for object attributes. Optional parameters are never, always
        'use-isnan': 2.// Disallow NaN for comparison, only isNaN()
        'default-case': 2.// Switch statements must end with default
        'newline-after-var': 2.// Whether the variable declaration should be followed by a blank line
        'max-depth': [2.4].// The depth of the nested block is up to four layers
        'max-params': [2.4].// A function can take a maximum of four arguments
        'no-else-return': 2.If (cond) {return a} else {return b} should be written if (cond) {return a} return b
        'no-eq-null': 2.// Disallow null with == or! = operator.
        'no-iterator': 2.// Disallow the __iterator__ attribute
        'no-mixed-spaces-and-tabs': [2.false].// Do not mix TAB and space
        'no-new-func': 1.// Disallow new Function
        'no-new-object': 2.// Disallow new Object()
        'no-self-compare': 2.// Cannot compare itself
        'no-unused-vars': [2, { vars: 'all'.args: 'after-used'}].// Can't make out unused variables or parameters
        'no-use-before-define': 0.// Cannot be used before definition
        'valid-typeof': 2.// Invalid type judgment
        'wrap-iife': [2.'inside'].// Execute function expressions immediately in parenthesis style
        // Comments are followed by slashes and asterisks with Spaces
        'spaced-comment': [
            2.'always',
            {
                block: {
                    exceptions: [The '*'].balanced: true,}},],// New, delete, typeof, void, yield, etc. must be preceded by Spaces (-, +, --, ++,!). ,!!!!! Must not have Spaces before or after the expression
        'space-unary-ops': [
            2,
            {
                words: true.nonwords: false],},'prefer-rest-params': 2.// Must use destruct... Args instead of arguments
        'consistent-this': [2.'self'.'that'].// This alias rule allows only self or that
        curly: [2.'multi-line'.'consistent'].// if must contain {, except for the single line if
        'for-direction': 2.// The for loop must not be dead loop due to the wrong direction
        'getter-return': [2, { allowImplicit: true}].Getters must have a return value, allowing undefined to be returned
        'keyword-spacing': 2.// The keyword must be preceded by Spaces
        // Class names should be capitalized after the new keyword
        'new-cap': [
            2,
            {
                capIsNew: false.// allow uppercase functions to be executed directly},].'no-await-in-loop': 2.// forbid "await" in loops
        'no-class-assign': 2.// The class name defined by class must not be the same as any other variable
        'no-dupe-args': 2.// Function arguments cannot have the same name
        'no-duplicate-case': 2.// Forbid the same case in switch
        'no-duplicate-imports': 2.// Disallow repeated import
        'no-empty-function': 0.// Disallow empty functions, including comments
        'no-empty-pattern': 2.// Disallow empty {} or [] in destructions
        'no-ex-assign': 2.// The parameter defined by catch disallows assignment
        'no-extend-native': [2, { exceptions: ['Array'.'Object']}],// Disable extension of native objects
        'no-extra-parens': [2.'functions'].// Disallow additional parentheses for function bodies only
        'no-floating-decimal': 2.// It is not allowed to use 2. Or.5 for numbers. The format is 2, 2.0, 0.5
        'no-func-assign': 2.// Disallows reassignment of function declarations
        'no-implied-eval': 2.// Disallow strings in setTimeout and setInterval, since implicit eval is triggered
        'no-multi-assign': 2.// Disallow continuous assignment
        '@typescript-eslint/explicit-function-return-type': [
            'off',
            {
                allowExpressions: true.allowTypedFunctionExpressions: true],},'@typescript-eslint/no-explicit-any': 0.In special cases, the type display can be set to any
        '@typescript-eslint/interface-name-prefix': 0.// Allow interface names to start with I
        '@typescript-eslint/no-var-requires': 0.// antd requires require to reference style
        '@typescript-eslint/no-use-before-define': 0.// mapStateToProps is used before (typeof inferred types)
        '@typescript-eslint/camelcase': 0.// Hump naming format
        '@typescript-eslint/no-empty-function': 0.// The default value for a function can be null
        'react/display-name': 0.// A puzzling Bug
        'react/no-find-dom-node': 0.'@typescript-eslint/no-non-null-assertion': 0.// Allow to use! Assertions are not null
    },
    settings: {
        // Automatically discover the React version to regulate the React code
        react: {
            pragma: 'React'.version: 'detect',}},parserOptions: {
        // Specifies that ESLint can parse JSX syntax
        ecmaVersion: 2019.sourceType: 'module'.ecmaFeatures: {
            jsx: true,,}}}Copy the code
  • Prettier /@typescript-eslint: Invalidation of the style specification in @typescript-esLint to conform to the style specification in prettier

  • Plugin: prettier/it: use the prettier in style specification, and if make ESLint will detect prettier the format of the questions, the same will be thrown in the form of the error format problem.

2.4 integrate ESLint configuration with VSCode

When you have this setup in your project, other developers need to install the ESLint and Prettier plugins in their own VSCode. ESLint and Prettier in VScode read the project configuration file to check the code. The pits are as follows:

  • It is important to remember that if you are working remotely from a work machineRemote VScode installation plug-inLocal installation doesn’t work.
  • Also, during team collaboration, plugin versions may be different, such as stable and unstable versions that parse ESLint rules differently, so the team directly tries to do as much as possibleInstall the same version of the plug-in.

2.4.1 Setting saves automatic verification

Prettier and ESLint can automatically check for and format some problems when they save, modifying their configuration file in settings.json as follows:

{
    "eslint.enable": true.// Whether to enable VScode eslint
    "eslint.options": { // specifies the suffix for files handled by vscode's eslint
        "extensions": [
            ".js".".vue".".ts".".tsx"]},"editor.formatOnSave": true."editor.codeActionsOnSave": {
        "source.fixAll.eslint": true
    },
    "eslint.validate": [ // Determine the verification criteria
        "javascript"."javascriptreact",
        {
            "language": "html"."autoFix": true
        },
        {
            "language": "vue"."autoFix": true
        },
        {
            "language": "typescript"."autoFix": true
        },
        {
            "language": "typescriptreact"."autoFix": true}}]Copy the code

2.5 Husky specification workflow

In the project in the process of migration and standardization, we cannot one-time all existing code migrated to TS, so we are in the process of actual use JS and TS mixed development, the needs of the business process in the actual changes file migration into TS, to has not yet met the code do not change, ensure the normal operation of the project. Similarly, formatting for Eslint focuses on newly developed pages. During development, in order to ensure that all team members can strictly implement the Eslint specification and build workflows using HUSky, Eslint will check the code that has been modified to exist in the stage phase and not yet in the COMMIT phase.

2.5.1 Installing Dependencies

    npm install husky --save-dev
Copy the code

2.5.2 configuration

Configure in package.json script:

 "scripts": {
   "lint": "npm run lint:js && npm run lint:style && npm run lint:prettier"."lint-staged": "lint-staged"."lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx "."lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src && npm run lint:style"."lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src"."lint:prettier": "check-prettier lint"."lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",}Copy the code

Next you need to configure husky in package.json as follows:

 "husky": {
   "hooks": {
      "pre-commit": "npm run lint"}},Copy the code

Lint will be checked before committing before the hook is pre-commit.

We did lint verification above by executing an NPM command in husky’s pre-comit hook. In general, we would define a lint-staged specification to test code before submission. To normalize code from Lint-staged, we modify the package.json file to:

{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"}},"lint-staged": {
    "**/*.{js,jsx,ts,tsx}": [
      "eslint --fix"."prettier --write"."git add ."]}}Copy the code

Note: Git /hooks folder, check to see if you have the pre-commit file (not the pre-commit. Sample file), if you don’t, The git version needs to be upgraded above 2.13.0.

Cicd is used for continuous integration in this project, so you can also add ESLint to CI, which I won’t go into here.

conclusion

The Typescript migration and Eslint+Prettier code formatting for the project have been live for a few months now and are working well.

This article is formatted using MDNICE