DEV Community

Cover image for Using ESLint and Prettier in a TypeScript Project
Robert Cooper
Robert Cooper

Posted on • Edited on • Originally published at robertcooper.me

Using ESLint and Prettier in a TypeScript Project

Originally posted on my blog.

When it comes to linting TypeScript code, there are two major linting options to choose from: TSLint and ESLint. TSLint is a linter than can only be used for TypeScript, while ESLint supports both JavaScript and TypeScript.

In the TypeScript 2019 Roadmap, the TypeScript core team explains that ESLint has a more performant architecture than TSLint and that they will only be focusing on ESLint when providing editor linting integration for TypeScript. For that reason, I would recommend using ESLint for linting TypeScript projects.


Setting up ESLint to work with TypeScript

First, install all the required dev dependencies:

yarn add eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin --dev

Next, add an .eslintrc.js configuration file in the root project directory. Here is a sample configuration for a TypeScript project:

module.exports =  {
  parser:  '@typescript-eslint/parser',  // Specifies the ESLint parser
  extends:  [
    'plugin:@typescript-eslint/recommended',  // Uses the recommended rules from the @typescript-eslint/eslint-plugin
  ],
 parserOptions:  {
    ecmaVersion:  2018,  // Allows for the parsing of modern ECMAScript features
    sourceType:  'module',  // Allows for the use of imports
  },
  rules:  {
    // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
    // e.g. "@typescript-eslint/explicit-function-return-type": "off",
  },
};

Note: I prefer using a JavaScript file for the .eslintrc file (instead of a JSON file) as it supports comments that can be used to better describe rules.

If using TypeScript with React, the eslint-plugin-react dev dependency should be installed and the following configuration can be used:

module.exports =  {
  parser:  '@typescript-eslint/parser',  // Specifies the ESLint parser
  extends:  [
    'plugin:react/recommended',  // Uses the recommended rules from @eslint-plugin-react
    'plugin:@typescript-eslint/recommended',  // Uses the recommended rules from @typescript-eslint/eslint-plugin
  ],
  parserOptions:  {
  ecmaVersion:  2018,  // Allows for the parsing of modern ECMAScript features
  sourceType:  'module',  // Allows for the use of imports
  ecmaFeatures:  {
    jsx:  true,  // Allows for the parsing of JSX
  },
  },
  rules:  {
    // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
    // e.g. "@typescript-eslint/explicit-function-return-type": "off",
  },
  settings:  {
    react:  {
      version:  'detect',  // Tells eslint-plugin-react to automatically detect the version of React to use
    },
  },
};

Ultimately it's up to you to decide what rules you would like to extend from and which ones to use within the rules object in your .eslintrc.js file.

Adding Prettier to the mix

What works well along with ESLint is prettier, which does a great job at handling code formatting. Install the required dev dependencies to get prettier working with ESLint:

yarn add prettier eslint-config-prettier eslint-plugin-prettier --dev

In order to configure prettier, a .prettierrc.js file is required at the root project directory. Here is a sample .prettierrc.js file:

module.exports =  {
  semi:  true,
  trailingComma:  'all',
  singleQuote:  true,
  printWidth:  120,
  tabWidth:  4,
};

Next, the .eslintrc.js file needs to be updated:

module.exports =  {
  parser:  '@typescript-eslint/parser',  // Specifies the ESLint parser
  extends:  [
    'plugin:@typescript-eslint/recommended',  // Uses the recommended rules from the @typescript-eslint/eslint-plugin
    'prettier/@typescript-eslint',  // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
    'plugin:prettier/recommended',  // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
  ],
  parserOptions:  {
    ecmaVersion:  2018,  // Allows for the parsing of modern ECMAScript features
    sourceType:  'module',  // Allows for the use of imports
  },
};

The advantage of having prettier setup as an ESLint rule using eslint-plugin-prettier is that code can automatically be fixed using ESLint's --fix option.

Automatically Fixing Code (VS Code)

For a good developer experience, it's useful to setup your editor to automatically run ESLint's automatic fix command (i.e. eslint --fix) whenever a file is saved. Since i'm using VS Code, here is the config required in the settings.json file in VS Code to get automatic fixing whenever saving a file:

"eslint.autoFixOnSave":  true,
"eslint.validate":  [
  "javascript",
  "javascriptreact",
  {"language":  "typescript",  "autoFix":  true  },
  {"language":  "typescriptreact",  "autoFix":  true  }
],

If you've also set the editor.formatOnSave option to true in your settings.json, you'll need to add the following config to prevent running 2 formatting commands on save for JavaScript and TypeScript files:

"editor.formatOnSave":  true,
"[javascript]":  {
  "editor.formatOnSave":  false,
},
"[javascriptreact]":  {
  "editor.formatOnSave":  false,
},
"[typescript]":  {
  "editor.formatOnSave":  false,
},
"[typescriptreact]":  {
  "editor.formatOnSave":  false,
},

And there you have it. That's how you can lint a TypeScript project using ESLint. If you want to make sure all the files you commit to git pass the ESLint checks, take a look at lint-staged, which can run ESLint on files being commited.

Latest comments (49)

Collapse
 
tonyfung99 profile image
Tony Fung

Hello, I have encountered an error that my eslint config conflict with the prettier one. Currently, I am trying to make my interface using comma as the delimiter, however, prettier enforce me to delete it. Is there any way to disable the prettier restriction on this rule?

My rule in .eslintrc

  '@typescript-eslint/member-delimiter-style': [
            'warn',
            {
                multiline: {
                    delimiter: 'comma',
                    requireLast: true,
                },
                singleline: {
                    delimiter: 'comma',
                    requireLast: false,
                },
            },
        ],
Enter fullscreen mode Exit fullscreen mode

.prettierrc

{
    "parser": "typescript",
    "trailingComma": "all",
    "semi": false,
    "singleQuote": true,
    "printWidth": 120,
    "tabWidth": 4    
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
rplaha0390 profile image
Raman

Did you find a solution to this? I have also run into the same issue? Thanks

Collapse
 
nmiddleweek profile image
Nick Middleweek

Hello, thanks for this article, it was super useful. I'm trying to find the answer to the question "Does it matter in which order the plugins are listed in the extends config array?"... Here's what I've ended up with, but I've noticed it's different to others on this thread. Thanks

"extends": [
    "plugin:@typescript-eslint/recommended",
    "plugin:react/recommended",
    "plugin:prettier/recommended",
    "prettier/@typescript-eslint",
    "google"
  ],
Collapse
 
vamshia03 profile image
vamshia03

Hi. I've added the config as below:

"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},

When I run "eslint --fix lib" it says

"Oops! Something went wrong! :(

ESLint: 6.8.0.

No files matching the pattern "lib" were found.
Please check for typing mistakes in the pattern."

All my files are in typescript which I migrated from javascript. The same command works for old .js files' directory but not the current new one.

Any advice?

Collapse
 
gmarokov profile image
Georgi Marokov

Thanks for the article! I've setuped Prettier with ESLint, but I'm thinking if there is a way to specify settings.json per project as I don't want to run eslint of every project I open and save.

Collapse
 
zalithka profile image
Andre Greeff

I'm battling a little bit with this one.. In addition to the typical src/ folder, my project also has a scripts/ folder which contains custom JS based build/test/release helper scripts to run with Node.

I placed an .eslintrc file in the project root with the TypeScript configuration, which appears to be working fine, but regardless of what I do in the scripts/.eslintrc file, the linter keeps trying to apply my TypeScript rules to my JaveScript files.

Isn't ESLint supposed to use the closest config file when linting subfolders? Is there any way to explicitly tell it do so?

Collapse
 
spock123 profile image
Lars Rye Jeppesen

I feel this article should be entitled "how to React with React and React".

Collapse
 
piavgh profile image
Hoang Trinh

Although I had configured Prettier and ESLint, I still need to add this rule in .eslintrc to have single quotes and no semicolon.

'prettier/prettier': ['error', { singleQuote: true, semi: false }],

If I remove this part { singleQuote: true, semi: false }, the editor will yell when I use single quotes or no semicolon.

This is my .prettierrc:

module.exports = {
  semi: false,
  trailingComma: 'es5',
  singleQuote: true,
  printWidth: 100,
  tabWidth: 2,
}

This is my .eslintrc extends:

extends: [
    'airbnb',
    'plugin:react/recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier/@typescript-eslint',
    'plugin:prettier/recommended',
  ],

According to this article prettier.io/docs/en/integrating-wi..., those 2 above should be enough.

Do you know why?

Collapse
 
brokenthorn profile image
Paul-Sebastian Manole • Edited

Thanks! It works perfectly!

Here's what I ended up with, since your examples don't build up (I mean the React config is lost after just being introduced :P):

module.exports = {
  parser: "@typescript-eslint/parser", // Allows ESLint to lint TypeScript.
  extends: [
    "plugin:react/recommended", // Uses the recommended rules from @eslint-plugin-react
    "plugin:@typescript-eslint/recommended", // A plugin that contains a bunch of ESLint rules that are TypeScript specific.
    'prettier/@typescript-eslint',  // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
    'plugin:prettier/recommended',  // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
  ],
  parserOptions: {
    ecmaVersion: 2018,
    sourceType: "module",
    ecmaFeatures: {
      jsx: true, // Allows for the parsing of JSX
    },
  },
  env: {
    es6: true,
    browser: true,
    node: true,
  },
  rules: {
    // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
    // e.g. "@typescript-eslint/explicit-function-return-type": "off",
  },
  settings: {
    react: {
      version: "detect", // Tells eslint-plugin-react to automatically detect the version of React to use
    },
  },
};

And of course, I disabled VSCode's onsave formatter, thanks for that catch! Could have driven me mad... :)

Collapse
 
john_papa profile image
John Papa

Thanks for the helpful post. I used this to make sure I didn't miss anything in mine. The extends were key for me.

Here is my repo github.com/johnpapa/vscode-peacock in case anyone wants another reference

Collapse
 
robertcoopercode profile image
Robert Cooper

Awesome! Glad you found it helpful 👍🏼

Collapse
 
waynevanson profile image
Wayne Van Son

THANK THE LORD FOR THIS! Eslint information is hard to decipher and this set up works great! Prettier is so simple and I'm glad to see that it can be leveraged with the ESlint engine.

I refer to this article everytime I start a project.. maybe it's time I start a template? ty xx