DEV Community

Eduardo Henrique Gris
Eduardo Henrique Gris

Posted on • Edited on

4

Typescript-eslint + prettier for code standardization in React with Typescript

Introduction

Portuguese version: Typescript-eslint + prettier para padronização de código em React com Typescript

Last month, I started part 1 of the basis for creating and publishing a React library with Typescript, where I'll be applying content I wrote last year. In the next part, I'll define the code standardization for the library, but instead of using what I wrote last year, which includes two articles with a React-focused approach using eslint with prettier (one on setup and another on rule customization), I'll use typescript-eslint and prettier in the library. For this reason, before moving on to part 2, I'm writing this article on that topic.

Libs

  • typescript-eslint: responsible for analyzing the code to identify and resolve issues
  • prettier: responsible for code formatting

Setup typescript-eslint

To add typescript-eslint:

yarn add typescript-eslint eslint @eslint/js --dev

  • typescript-eslint: allows eslint to parse typescript syntax and provides linting rules for typescript.
  • eslint: dependency required by typescript-eslint
  • @eslint/js: provides eslint rules

Generate a configuration file at the root of the project:

  • eslint.config.mjs
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";

export default tseslint.config(
  eslint.configs.recommended,
  tseslint.configs.recommendedTypeChecked,
  {
    languageOptions: {
      parserOptions: {
        projectService: true,
        tsconfigRootDir: import.meta.dirname,
      },
    },
  },
  {
    settings: {
      react: {
        version: "detect",
      },
    }
  }
);
Enter fullscreen mode Exit fullscreen mode
  • tseslint.config: where the typescript-eslint configurations are specified
  • eslint.configs.recommended: applies the recommended eslint rules in the code analysis
  • tseslint.configs.recommendedTypeChecked: applies the recommended typescript rules in the code analysis, that additionally require type information
  • projectService: indicates to ask typescript's type checking service for each source file's type information
  • import.meta.dirname: tells the parser the absolute path of project's root directory
  • settings: it is set to detect the version of React being used in the project

Adding plugins

The following libraries will be added to apply React and hooks rules:

yarn add eslint-plugin-react eslint-plugin-react-hooks --dev

  • eslint-plugin-react: brings linting rules for React
  • eslint-plugin-react-hooks: brings hooks rules

Adding the plugins in the eslint configuration file:

  • eslint.config.mjs
// @ts-nocheck

import eslint from "@eslint/js";
import reactPlugin from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import tseslint from "typescript-eslint";

export default tseslint.config(
  eslint.configs.recommended,
  tseslint.configs.recommendedTypeChecked,
  {
    languageOptions: {
      parserOptions: {
        projectService: true,
        tsconfigRootDir: import.meta.dirname,
      },
    },
  },
  reactPlugin.configs.flat.recommended,
  reactPlugin.configs.flat['jsx-runtime'],
  {
    settings: {
      react: {
        version: "detect",
      },
    },
    plugins: {
      "react-hooks": reactHooks,
    },
    rules: {
      ...reactHooks.configs.recommended.rules,
    }
  }
);
Enter fullscreen mode Exit fullscreen mode
  • // @ts-nocheck: Used to ignore the type check for this file due to an issue with eslint-plugin-react-hooks related to missing type declarations
  • reactPlugin.configs.flat.recommended: applies the recommended React rules in the code analysis
  • reactPlugin.configs.flat['jsx-runtime']: required for React 17+ to work with the new jsx runtime introduced in that version
  • plugins: add the eslint-plugin-react-hooks plugin to extend eslint
  • rules: add customizable rules, in the case of eslint-plugin-react-hooks, the recommended rules are included

Adding prettier

The lib will be added:

yarn add prettier --dev

A configuration file named .prettierrc is created at the root, initially empty since the default rules of the library are not being modified.

Customization of rules

At the moment, the configuration of typescript-eslint and prettier has been done, applying the recommended rules from the added plugins and the default from prettier. However, inside a project, some of these rules might be interesting to modify, and this can be done through the eslint and prettier configuration file.

Prettier customization

To customize prettier rules, use the following structure:

{
  rule: rule_expectation,
  rule: rule_expectation
}
Enter fullscreen mode Exit fullscreen mode
  • rule: corresponds to the rule that will be customized
  • rule_expectation: corresponds to what is expected for the code to follow

At the moment, the eslint configuration file is using the default prettier rules, among which are some rules to not use single quotes for code and jsx. Starting from an example where the project wants to define the use of single quotes, it would look like this in the configuration file:

  • .prettierrc
{
  "singleQuote": true,
  "jsxSingleQuote": true
}
Enter fullscreen mode Exit fullscreen mode

Eslint customization

To customize eslint rules, use the following structure:

"rules": {
  rule: error_type,
  rule: [error_type, rule_expectation]
}
Enter fullscreen mode Exit fullscreen mode
  • rule: corresponds to the rule that will be customized
  • error_type: it can be warn, which returns a warning if the rule is not satisfied; error, which returns an error if the rule is not satisfied; or off, which disables the rule
  • rule_expectation: corresponds to what is expected for the code to follow

In the case of a rule that already has an expectation defined inside it, rule: error_type is passed directly. For rules that can have multiple expectations, rule: [error_type, rule_expectation] is passed, defining what is expected from it.

At the moment, the eslint configuration file is using the recommended rules from eslint, typescript-eslint, react and react-hooks. Starting from an example where the project wants to define a customizable rule for each of them, it would look like this in the configuration file:

// @ts-nocheck

import eslint from "@eslint/js";
import reactPlugin from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import tseslint from "typescript-eslint";

export default tseslint.config(
  eslint.configs.recommended,
  tseslint.configs.recommendedTypeChecked,
  {
    languageOptions: {
      parserOptions: {
        projectService: true,
        tsconfigRootDir: import.meta.dirname,
      },
    },
  },
  reactPlugin.configs.flat.recommended,
  reactPlugin.configs.flat['jsx-runtime'],
  {
    settings: {
      react: {
        version: "detect",
      },
    },
    plugins: {
      "react-hooks": reactHooks,
    },
    rules: {
      ...reactHooks.configs.recommended.rules,
      "no-console": "warn", // eslint rule
      "react/jsx-no-useless-fragment": "error", // React rule
      "react-hooks/exhaustive-deps": "off", // hooks rule
      "@typescript-eslint/no-unused-vars": "off", // typescript rule
    }
  }
);
Enter fullscreen mode Exit fullscreen mode

Ignore files

There will be certain types of files for which you don't want the typescript-eslint to run. To do this, you can add ignores in the configuration file. As an example, for an app that ignores documentation files with storybook in the standardization, it would look like this:

// @ts-nocheck

import eslint from "@eslint/js";
import reactPlugin from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import tseslint from "typescript-eslint";

export default tseslint.config(
  eslint.configs.recommended,
  tseslint.configs.recommendedTypeChecked,
  {
    languageOptions: {
      parserOptions: {
        projectService: true,
        tsconfigRootDir: import.meta.dirname,
      },
    },
  },
  reactPlugin.configs.flat.recommended,
  reactPlugin.configs.flat['jsx-runtime'],
  // the rules will not apply to these types of files
  {
    ignores: ["**/*.stories.tsx"],
  },
  {
    settings: {
      react: {
        version: "detect",
      },
    },
    plugins: {
      "react-hooks": reactHooks,
    },
    rules: {
      ...reactHooks.configs.recommended.rules,
      "no-console": "warn",
      "react/jsx-no-useless-fragment": "error",
      "react-hooks/exhaustive-deps": "off",
      "@typescript-eslint/no-unused-vars": "off",
    }
  }
);
Enter fullscreen mode Exit fullscreen mode

Specific rules for certain types of files

In addition to defining files to exclude from running typescript-eslint, it's also possible to define customizable rules specifically for certain files by setting them alongside files. As an example, for an app that disables a rule for test files, it would look like this:

// @ts-nocheck

import eslint from "@eslint/js";
import reactPlugin from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import tseslint from "typescript-eslint";

export default tseslint.config(
  eslint.configs.recommended,
  tseslint.configs.recommendedTypeChecked,
  {
    languageOptions: {
      parserOptions: {
        projectService: true,
        tsconfigRootDir: import.meta.dirname,
      },
    },
  },
  reactPlugin.configs.flat.recommended,
  reactPlugin.configs.flat['jsx-runtime'],
  {
    ignores: ["**/*.stories.tsx"],
  },
  {
    settings: {
      react: {
        version: "detect",
      },
    },
    plugins: {
      "react-hooks": reactHooks,
    },
    rules: {
      ...reactHooks.configs.recommended.rules,
      "no-console": "warn",
      "react/jsx-no-useless-fragment": "error",
      "react-hooks/exhaustive-deps": "off",
      "@typescript-eslint/no-unused-vars": "off",
    }
  },
  // specific files with specific rules for them
  {
    files: ["**/*.test.tsx"],
    rules: {
      "@typescript-eslint/no-unused-expressions": "off",
    },
  }
);
Enter fullscreen mode Exit fullscreen mode

Script package.json

To run eslint and check the application's code, run prettier and check the application's formatting, scripts will be added to the package.json:

// ...
"scripts": {
  // ...
  "lint": "eslint .",
  "lint-fix": "eslint . --fix",
  "lint-src": "eslint src",
  "lint-src-fix": "eslint src --fix",
  "format": "prettier . --check",
  "format-fix": "prettier . --write"
  "format-src": "prettier src --check",
  "format-src-fix": "prettier src --write"
}
Enter fullscreen mode Exit fullscreen mode
  • lint: it will check all the application's files, following the rules defined in the eslint configuration file
  • lint-fix: it will auto-correct the code for the entire application, following the rules defined in the eslint configuration file
  • lint-src: it will check all the files inside the src folder (I used src as an example, but it could be any directory inside the app where these checks are necessary), following the rules defined in the eslint configuration file
  • lint-src-fix: it will auto-correct all the files inside the src folder, following the rules defined in the eslint configuration file
  • format: it will check all the application's files formatting, following the rules defined in the prettier configuration file
  • format-fix: it will auto-correct the formatting for the entire application, following the rules defined in the prettier configuration file
  • format-src: it will check all the files inside the src folder formatting (I used src as an example, but it could be any directory inside the app where these checks are necessary), following the rules defined in the prettier configuration file
  • format-src-fix: it will auto-correct the formatting for all the files inside the src folder, following the rules defined in the prettier configuration file

Article updates

Initially, the article had configured typescript-eslint to run together with prettier, it was using tseslint.configs.recommended instead of tseslint.configs.recommendedTypeChecked and mentioned that it wasn't possible to use the eslint-plugin-react-hooks plugin. However, Josh Goldberg, a team member of the linters, pointed out three interesting points that made me update the article:

For this reason, I modified it to use tseslint.configs.recommendedTypeChecked, separated typescript-eslint from prettier (each with its own responsibility) and added eslint-plugin-react-hooks.

Conclusion

The idea of this article is to present how to configure typescript-eslint and prettier, adding plugins, and showing how to customize rules. In part 2 of the component library configuration, this article will be used as a basis to define the code standardization rules.

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)