Paul Ccari I'm a frontend engineer, UI/UX designer enthusiast, and content creator who has a passion for learning, sharing, and building cool things.

Linting in TypeScript using ESLint and Prettier

8 min read 2427

Linting In TypeScript Using ESLint And Prettier

Editor’s note: This article was last updated on 16 December 2022. Check out this Using Prettier and ESLint to automate formatting tutorial for more information.

As developers, we usually start a project by adding configurations and scripts for linting, then formatting and type checking for our codebase syntax and style.

This process is even more important when working on a team because everyone needs to be on the same page for codebase syntax and style. Also, to ensure that there are no typos or bugs when our app is in production, we should type-check our code along the way.

Having a formatting tool, a linting tool, and TypeScript helps us automate this process.

In this article, we’ll use ESLint and TypeScript, and we’ll also see how to add Prettier and additional tooling that will automate this process for us.

To jump ahead:

Note: There is a compatibility issue with specific versions of Node and the latest versions of ESLint and Prettier, so make sure you’re running a Node version >= 16.0.0 when following the code sections later.

Compiling TypeScript code

Typescript is a superset of JavaScript that helps us with static type checking at compile time. TypeScript will give you a better development experience thanks to auto-completion in your editor. TypeScript also helps you maintain the code in your large codebases.

First, we need a compiler to turn TypeScript code into JavaScript so the browser can read it. Let’s install a dependency using your favorite package manager. Head over to a suitable folder on your computer and run one of the following commands in your terminal:

#npm
npm install --save-dev typescript

#yarn
yarn add typescript --dev 

Upon installation, you’ll see a new entry to the devDependencies attribute of your package.json file as:

{
  "name": "Linting TypeScript with ESLint",
  "version": "1.0.0",
  "devDependencies": {
    "typescript": "^4.9.4"
  }
}

If you want to verify that it’s been installed, you can run this to check the version:

npx tsc --version
# Version 4.4.3

In your folder, create an index.ts file in the src directory and add the following TypeScript code:

// src/index.ts
const favoriteFruits: string[] = ["apple", "strawberry", "orange"];

function addFruit(fruit: string) {
  favoriteFruits.push(fruit);
}

We can compile the TypeScript code to JavaScript by running the following command in the terminal:

npx tsc src/index.ts

Right after that, we’ll see a newly generated JavaScript file in the same directory as the TypeScript file:

// src/index.js
var favoriteFruits = ["apple", "strawberry", "orange"];

function addFruit(fruit) {
    favoriteFruits.push(fruit);
}

By default, the compiler will create JavaScript files side-by-side with the TypeScript source file that created it. This is not a good idea because you’ll end up mixing your build results with your source code.

So, let’s change some default compiler settings, starting from where we want to put our compiled code, which JavaScript level is targeted to be transpiled (by default: ECMAScript 3), and which files we want to compile.

There are two ways to create your TypeScript compiler settings:

  1. Run the following command in the terminal: npx ts``c --init. This will generate a default TypeScript configuration file
  2. Create a file called tsconfig.json at the root directory of your project and include your settings

In this case, I’ll create the TypeScript compiler settings manually. However, I would encourage you to choose the first option. It will create the config file with some recommended options — all options are described with comments explaining what they do.

You can modify these settings as you need to. See the full list of supported compiler options, and you can play around in the TypeScript playground:

// tsconfig.json
{
  "compilerOptions": {
    "outDir": "dist", // where to put the compiled JS files
    "target": "ES2020", // which level of JS support to target
    "module": "CommonJS", // which system for the program AMD, UMD, System, CommonJS

    // Recommended: Compiler complains about expressions implicitly typed as 'any'
    "noImplicitAny": true, 
  },
  "include": ["src"], // which files to compile
  "exclude": ["node_modules"], // which files to skip
}

Congratulations! Now, you can start writing TypeScript and compiling it to JavaScript by running npx tsc.

You can include the above command in your scripts to make it easier to run. Go to package.json and add the --watch flag to watch the files for changes. Keep in mind that everything that is described in compilerOptions can be passed into the command line using CLI flags:

// package.json
{
  "name": "Linting TypeScript with ESLint",
  "version": "1.0.0",
  "devDependencies": {
    "typescript": "^4.9.4"
  },
  "scripts": {
    "dev": "tsc --watch"
  }
}

What is ESLint?

One of the most popular tools for linting is ESLint, which will analyze your code to find potential bugs and improve your code quality by defining coding conventions and then automatically enforcing them. Let’s see how to install ESLint into our TypeScript project.

First, install the following dependencies to your devDependencies:

npm install eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin --save-dev
  • eslint: ESLint core library
  • @typescript-eslint/parser: a parser that allows ESLint to understand TypeScript code
  • @typescript-eslint/eslint-plugin: plugin with a set of recommended TypeScript rules

Similar to Typescript compiler settings, you can either use the command line to generate a configuration file using the --init flag from ESLint or create it manually. Either way, it’s mandatory to have your ESLint configuration file.

Let’s create a configuration file using the CLI. Run the following command in the terminal:

npx eslint --init

Next, you will see a series of questions that allow you to adjust the configuration file based on your preferences:

  • How would you like to use ESLint?
  • What type of modules does your project use?
  • Which framework does your project use?
  • Does your project use TypeScript?
  • Where does your code run?
  • How would you like to define a style for your project?

Based on the options selected, the ESLint CLI will create a .eslintrc.json configuration file in the project’s root directory with some default settings. If you already have your favorite settings, you can replace some of the options in the configuration file.





You can also explore the full list of ESLint settings available for the configuration file.

Linting with ESLint

For this article, replace the default settings in the configuration file with this:

// .eslintrc
{
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": 12,
    "sourceType": "module"
  },
  "plugins": ["@typescript-eslint"],
  "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
  "rules": {

  },
  "env": {
    "browser": true,
    "es2021": true
  },
}
  • parser: this specifies a parser for ESLint to use when analyzing the code
  • parserOptions: specifies what JS language options you want to support, such as the version of ECMAScript syntax you want to use
  • plugins: this is where you define plugins to use
  • extends: tells ESLint what configuration is set to extend from. The order matters, as the last extend option will override the previous ones in any conflicting configurations.
  • env: which environments your code will run in

When we add an ESLint rule, it overrides the configuration defined in the extends list. Let’s add a couple of rules to see how it works:

// .eslintrc
{
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": 12,
    "sourceType": "module",
  },
  "plugins": ["@typescript-eslint"],
  "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],

  "rules": {
    "@typescript-eslint/no-unused-vars": "error",
    // to enforce using type for object type definitions, can be type or interface 
    "@typescript-eslint/consistent-type-definitions": ["error", "type"], 
  },

  "env": {
    "browser": true,
    "es2021": true
  }
}

In short, the first rule we apply is assigned to a value of error, but error is not the only value we can assign — we have three options:

  • off or 0: turn off the rule completely
  • warn or 1: treat the rule as a warning, but it won’t fail when running a linter
  • error or 2: treat the rule as an error. It will fail when running a linter

Note: In some ESLint rules, like the second rule, you would need to set additional options to use an array literal syntax.

You can tell ESLint to lint your code using the following command: eslint --ext .js,.ts. The ext flag is used to specify which file extensions ESLint should consider when searching for files in the target directory. In this case, we include TypeScript file extensions: .ts (by default, it’s .js)

Now you can add a lint script into your package.json with the command above:

// package.json
{
  "name": "Linting TypeScript with ESLint",
  "version": "1.0.0",
  "scripts": {
    "dev": "tsc --watch",
    "lint": "eslint --ext .js,.ts .",
  },
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^5.45.1",
    "@typescript-eslint/parser": "^5.45.1",
    "eslint": "^8.29.0",
    "typescript": "^4.9.4"
  }
}

You’ll find that some files don’t need to be linted at all, such as your dist folder, so you can prevent linting by creating a .eslintignore file and adding the folders or files you want to ignore:

node_modules
dist

This often matches with your .gitignore file content, so to have a single source of truth, you can update the lint script to use the --ignore-path flag:

// package.json
{
  // ...
  "scripts": {
    "lint": "eslint --ignore-path .eslintignore --ext .js,.ts ."
   },
  // ...
}

Now you’re ready to go! I suggest you integrate ESLint into whatever editor you use. If that’s VSCode, go to the extension and install the ESLint extension.

Once you’ve installed and enabled it, you’ll see what errors you’ve made in your code without running the script that is underlining with a red line.

Note: You’ll see that the ESLint error message is printed inline in the editor; that’s another extension called Error Lens, which highlights the entire line and shows the error message immediately instead of hovering with the pointer to see it:

ESLint Error Message

Another feature of ESLint is that it can automatically fix code when you hover and right-click Quick fix, or you can hit command and +:

ESLint Quick Fix

Manually fixing all of the errors that have broken your rules can be tedious, but you can run the following command that will tell ESLint to fix what it can:

npm run lint -- --fix

Tip: You can pass parameters using double dashes -- for npm scripts, which will be received as parameters for the script that npm executes:

npm run <command> [-- <args>]

What is Prettier?

Prettier is a well-known code formatter that supports a variety of different programming languages. It helps us avoid manually formatting our code by automatically formatting it based on a specified code style.

Nowadays, it’s common to use ESLint and Prettier together, and we will learn how to integrate Prettier with ESLint. First, let’s look at the difference between both and why they can be beneficial together.

Why do we need Prettier with ESLint?

The primary function of a linter is to improve your code by analyzing it and alerting you to any potential issues based on customizable or pre-defined rulesets. These rulesets, or rules, allow development teams to maintain a consistent coding style and identify potential bugs caused by inconsistent coding styles.

On the other hand, a code formatter like Prettier ensures a consistent style by parsing your code and re-printing it according to its rules. For example, you can specify a style that all JavaScript statements must end with a semicolon; the code formatter will automatically add the semicolon to all statements without one.

In essence, you can use ESLint to specify rulesets that must be adhered to and then use Prettier to fix cases in your code where these rulesets are broken.

Integrating Prettier

With that covered, let’s add Prettier to our project; run the following command in the terminal:

npm install --save-dev prettier

Compared to ESLint, Prettier doesn’t need a config file, meaning you can run and use it straight away. However, If you want to set a config, you will need to create a file called .prettierrc (in the project’s root directory), where you can define your format options.


More great articles from LogRocket:


You can take a look at the full list of format options and can play around in the Prettier Playground:

// .prettierrc
{
  "semi": false, // Specify if you want to print semicolons at the end of statements
  "singleQuote": true, // If you want to use single quotes
  "arrowParens": "avoid", // Include parenthesis around a sole arrow function parameter
}

Next, we are going to start formatting our code using Prettier in the command line:

npx prettier --write src/index.ts
# src/index.ts 37ms

I added a write flag to overwrite the TypeScript file, otherwise, it won’t overwrite it and will only log the formatted code in your CLI.

Let’s add the Prettier command to our scripts, just as we did for TypeScript and ESLint. Let’s also support all files that end in .ts, .js, and .json, and ignore the same files and directories as gitignore (or you can create a file .prettierignore):

// package.json

{
  // ...
  "scripts": {
    "dev": "tsc --watch",
    "lint": "eslint --ext .js,.ts .",
    "format": "prettier --ignore-path .gitignore --write \"**/*.+(js|ts|json)\""
  },
  // ...
}

Now, you can run the npm run format command to format and fix all your code. But what if we would like to format the code right after saving your files?

That’s possible! In VSCode, go to the extensions tab, look for the Prettier extension, and ensure it’s enabled. Once enabled, we need to configure a few things in VSCode.

You can open your command palette (Command + Shift + P) and look for Preferences: Open Settings (JSON). Then you’ll need to change your editor default formatter and add an extra config to format code when you save your files:

// settings.json
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  ...
}

Preview Prettier Extension

Avoiding conflicts when working with ESLint and Prettier

You’ll likely run into an issue when a Prettier and ESLint rule overlap. You can try to auto-format your code, but it will show you some conflicts with ESLint.

The best solution here is to use a plugin eslint-config-prettier to disable all ESLint rules that are irrelevant to code formatting, as Prettier is already good at it:

npm install --save-dev eslint-config-prettier

With that installed, let’s go to the .eslintrc file and prettier at the end of your extends list to disable any other previous rules from other plugins:

// .eslintrc
{
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": 12,
    "sourceType": "module",
  },
  "plugins": ["@typescript-eslint"],
  // HERE
  "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],

  "rules": {
    "@typescript-eslint/no-unused-vars": "error",
    "@typescript-eslint/consistent-type-definitions": ["error", "type"],
  },

  "env": {
    "browser": true,
    "es2021": true
  }
}

That’s it! Now you know how to use these static testing tools effectively. It’s great to have some automation for specific tasks like linting, formatting, and type-checking.

Conclusion

Using TypeScript and ESLint together can boost our confidence in our code. It helps us prevent bugs and can save us time in the long run. I recommend you try using TypeScript and ESLint for a better developer experience for you and your entire team the next time you’re building cool things.

: Full visibility into your web and mobile apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.

.
Paul Ccari I'm a frontend engineer, UI/UX designer enthusiast, and content creator who has a passion for learning, sharing, and building cool things.

2 Replies to “Linting in TypeScript using ESLint and Prettier”

Leave a Reply