There’s enough on our plates as engineers without having to worry about code conventions, styling and formatting. I’m a big proponent of having as much of this happen at the press of a button as possible, so I can focus on the bigger picture.

There’s no reason to be using engineers’ time to make manual amendments that could be fixed automatically.


What is code linting?

Linting is the name given to the process of automatically analysing your source code and highlighting issues such as:

  • Syntax errors
  • Bugs
  • Failure to follow stylistic conventions
  • Failure to follow best practices

In addition to linting, many projects opt to use an opinionated code auto-formatter to make sure that all files in the codebase follow a common set of stylistic rules. Linters can also have some rules relating to formatting, so the lines between the two can be blurred to some extent. That being said, a code formatter is likely to have much more detailed rules and extensive functionality with regards to formatting, whereas a linter will have a lighter touch.

How do linting and auto-formatting help?

There are clear benefits to using these tools:

  • Catching bugs and issues as early in the development cycle as possible
  • Educating engineers about best practices
  • Taking the guesswork out of how to style code
  • Providing a consistent experience across a codebase, so engineers can focus on the content of the code, not how it is presented
  • Benefitting from the shared experience of the entire software engineering community that has contributed to the development of the linting rules
  • Reducing friction when onboarding new members to the software development team
  • Being in line with industry standards

How to set up and configure linting and auto-formatting in TypeScript

1. Install ESlint and Prettier

The go-to tools for linting and formatting in TypeScript are eslint and prettier, respectively.

On the assumption that you already have a TypeScript project up and running, we need to install a fairly bewildering set of dependencies:

npm i --save-dev \
  eslint \
  prettier \
  eslint-config-airbnb-base \
  eslint-config-airbnb-typescript \
  eslint-plugin-jest \
  eslint-config-prettier \
  eslint-plugin-import \
  eslint-plugin-prettier \
  @typescript-eslint/eslint-plugin \
  @typescript-eslint/parser

A quick explanation of what all this is for:

  • eslint / prettier - the main libraries required
  • eslint-config-airbnb-base - eslint comes with its own built-in rules, but AirBnB released their own additional recommended ruleset. Over the years, this ruleset has become the de facto industry standard so is always a great place to start. The "base" version covers NodeJs projects, and a "react" version is available as well.
  • eslint-config-airbnb-typescript - TypeScript support for the AirBnB ruleset.
  • eslint-plugin-jest - extra linting rules focused on jest tests.
  • eslint-config-prettier - stops eslint and prettier from disagreeing on how your code should be formatted!
  • eslint-plugin-import - enables eslint to help you with module imports.
  • eslint-plugin-prettier - integrates prettier with eslint, more about that below.
  • @typescript-eslint/eslint-plugin - eslint rules for TypeScript.
  • @typescript-eslint/parser - allows eslint to parse your TypeScript files.
Phew!

2. Configuration

The key to what we are going to do here is to run eslint and prettier together so that eslint will flag to you when prettier identifies a formatting problem. That way, you only have to interact directly with eslint, whether that be in your IDE or on the command line.

Add a `.eslintrc.json` file to your project root with the following content:

{
 "env": {
   "node": true,
   "es6": true,
   "jest": true,
   "jest/globals": true
 },
 "extends": [
   "eslint:recommended",
   "plugin:@typescript-eslint/recommended",
   "plugin:@typescript-eslint/recommended-requiring-type-checking",
   "airbnb-base",
   "airbnb-typescript/base",
   "plugin:jest/recommended",
   "prettier"
 ],
 "parser": "@typescript-eslint/parser",
 "parserOptions": {
   "ecmaVersion": 2021,
   "sourceType": "script",
   "project": ["./tsconfig.json"]
 },
 "plugins": ["@typescript-eslint", "prettier", "jest"],
 "rules": {
   "prettier/prettier": [
     "error",
     {
       "singleQuote": true,
       "trailingComma": "all",
       "endOfLine": "auto"
     }
   ]
 }
}

This file is the central place for configuring eslint and will be discovered automatically. It configures eslint to use all of the various dependencies we previously installed.

Notes:

  • if your repository contains multiple TypeScript “projects”, you can add additional `tsconfig` file paths into the `parserOptions.project` array to include them all.
  • if your project is front end rather than NodeJs, set `browser: true` rather than `node: true` within `env`.

3. Integrating with VSCode

Helpfully there are extensions to help us work with eslint and Prettier:

Once installed, you will see red lines under your code from eslint when there are any problems you need to resolve. Because we have integrated prettier into eslint, this will include formatting issues as well.

A benefit of this is the ability to tell eslint to magically fix everything it can for you automatically. This will, for example, automatically change a `var` to a `const`. Additionally, it will prompt prettier to automatically resolve any formatting issues for you. This can be done via the `ESLint: Fix all auto-fixable problems` command that is accessible via the Command Palette (Ctrl/Cmd + Shift + P).

I use this command hundreds of times a day - I pay little attention to formatting when I am writing code. It’s simply a waste of time for me to worry about it. To speed up the process, I would strongly recommend binding the auto-fix command to a key-binding. I use `Cmd + E`. Then you can code with reckless abandon, and tap a quick keyboard shortcut to have eslint and prettier deal with the aftermath on your behalf!

A simple example:

Before
Cmd + E
After
Fixed!

4. Running via the command line

Eslint can run via the command line which can be helpful for running in CI. Here are some suggested npm scripts to add to your project's package.json file:

 "scripts": {
   "lint": "eslint --ext .js,.ts ./",
   "lint:fix": "eslint --ext .js,.ts --fix ./",
   "lint:changed": "eslint $(git diff --name-only --diff-filter=ACMRTUXB main | grep  -E \"(.js$|.ts$)\")",
   "lint:changed:fix": "eslint --fix $(git diff --name-only --diff-filter=ACMRTUXB main | grep  -E \"(.js$|.ts$)\")"
 },
  • `lint` will run eslint over all js and ts files in your project
  • `lint:fix` will do the same but in auto-fix mode. Eslint will automatically fix any issues it can while running its scan. This includes prettier formatting thanks to our config!
  • `lint:changed` same as `lint` but only processes files that have been modified on your current git branch (when compared to the `main` branch). This can be very helpful for speeding up eslint checks in a large project, as you are only processing files relevant to your changes.
  • `lint:changed:fix` same as `lint:changed` but with auto-fixing on top!

5. Ways of working

Now all that is left is to agree with your team how you are going to make sure the code is kept in line with the linter. Here are some suggestions that have worked well for the teams I’ve been part of over the years:

  • Run the linting as part of merge/pull request CI checks and do not allow failing code to be merged. Once code starts diverging from the agreed standard, you’re giving future contributors a headache to deal with.
  • Read and understand the rationale for linting rules that are erroring on your code. Eslint has helpful documentation for every rule which you can link to straight from the error in VSCode. You’ll probably learn something new whilst reading it! Only put an ignore in place if you judge the rule to not be applicable to your use case for a good reason, and be ready to explain that reasoning to reviewers of your work. Even better - proactively add comments to your pull/merge request explaining why a rule was judged not to apply. And if you’re a reviewer - ask when you see an ignore statement that doesn’t make clear sense.
  • If you’re having to frequently ignore a certain rule, you should consider making changes to your eslint configuration. Rules can be turned off or re-configured. It is better to spend time making sure the configuration works for you, rather than you and all your colleagues spending time adding ignore statements.
  • One size doesn’t fit all. For example, a rule that makes sense in your implementation code might not be relevant in your unit tests. In your eslint configuration, you can apply different rule settings for different sets of files, so you can be sure you’re applying the right set of conventions in the right areas of your codebase.
Happy linting!

Author: Rick Clark