How to use Prettier with ESLint and TypeScript in VSCode

Last updated Jan 21st, 2022
In this guide, we'll explain how to use Prettier with ESLint, delegating the responsibility of code convention definition to ESLint, and the responsibility of formatting to Prettier.

This post is a part of the Clean Code Tooling series.
You may want to read the previous post.
1. How to use ESLint with TypeScript

Intro

In the previous article in this series, "How to use ESLint with TypeScript", we learned how add ESLint to our project and configure it to determine if our code was adhering to our project's coding conventions.

This is great, but it can get pretty annoying having to re-run npm run lint everytime we want lint (and optionally fix) our code.

Here's where Prettier, particularly the VS Code extension for Prettier, comes in.

Understanding Prettier

Prettier is an opinionated (yet fully configurable) code formatter. ESLint can kind of format code too, but it's mostly intended to sniff out when we're not following the mandated coding conventions.

Prettier can be configured to format your code (makes it look prettier 😉) after you save a file or manually tell it to, and by default, it comes configured with a set of common code cleanliness rules.

We get the most benefit out of Prettier when we combine it with ESLint though.

Using Prettier with ESLint

Combining both ESLint and Prettier, the responsibility division is this:

  • ESLint defines the code conventions
  • Prettier performs the auto-formatting based on the ESLint rules

Now that's a bomb combo.

Goals for this blog post

At the end of this post, you should be able to:

  • Set up Prettier for a TypeScript or JavaScript project
  • Decide on the formatting configuration that best suits your style
  • Configure Prettier to work with ESLint

Prerequisites

Installing Prettier

First thing's first, we'll install Prettier as a dev dependency.

npm install --save-dev prettier

Configuring Prettier

As per the docs, we can expose a JSON file called .prettierrc to put our configuration within.

touch .prettierrc

A basic .prettierrc setting is the following:

.prettierrc
{
  "semi": true,
  "trailingComma": "none",
  "singleQuote": true,
  "printWidth": 80
}

These settings specify the following rules:

  • semi set to true means that Prettier will add semicolons when necessary.
  • trailingComma set to none means that Prettier will remove any trailing commas at the end of objects.
  • singleQuote set to true means that Prettier will prefer single quotes instead of double quotes unless the number of double-quotes outweighs the number of single-quotes. Read more here.
  • printWidth set to 80 specifies that the printer will wrap any lines that exceed 80 characters.

You can view the rest of the options here and change them as you like! This is just my personal preference.

Testing Prettier using the Prettier CLI

So far, we've locally configured Prettier, and we're in shape to test formatting some code.

Since we're using the code from simple-typescript-starter, the only file we have is src/index.ts, and it looks like this:

console.log('Hello')

When we add a script to format all the code in the folder and execute it, the only change we should notice is an added semicolon.

Add the following script to your package.json.

package.json
{
  "scripts": {
    ...
    "prettier-format": "prettier --config .prettierrc 'src/**/*.ts' --write"  }
}

And then run it.

npm run prettier-format

The console output shows that one file has been changed and that it took 186ms to execute.

npm run format

> typescript-starter@1.0.0 format /simple-typescript-starter
> prettier --config .prettierrc 'src/**/*.ts' --write

src/index.ts 186ms

Looking at src/index.ts, you'll notice that the console.log was, in fact, appended with a semicolon.

console.log('Hello world!');

This is a neat way to use Prettier, but it's not the best, in my opinion. Here's what I prefer.

Using Prettier

One of the most common ways that people use Prettier is to install the VS Code extension that adds it to your editor.

It enables you to, on save, format your code. This is good because the feedback loop is incredibly short, and it turns formatting into something that you don't have to occupy any brain cycles thinking about.

Install the Prettier VS Code extension here.

To set the defaults, press CMD + SHIFT + P (on MacOS) or CTRL + Shift + P (on Windows), then type in preferences open settings.

You want to select the JSON option so that we can manually edit the preferences via a JSON file.

Options for typing in "preferences open settings". You want to select the JSON option.

In the JSON file, if it's not already added, add the following lines to the root of the object.

// Default (format when you paste)
"editor.formatOnPaste": true,
// Default (format when you save)
"editor.formatOnSave": true,

These settings will format your code both when you paste new code and when you save code for any file extension that Prettier understands. If the root of the project that the file lives in has a .prettierrc, it will use the settings that live in that file to format your code.

Let's say you're working with multiple languages and automatically formatting code in another language is a no go for you. You can also override the default settings by specifying the language as shown below.

"[typescript]": {
  "editor.formatOnPaste": false,  "editor.formatOnSave": false,},
"editor.formatOnPaste": true,
"editor.formatOnSave": true,

Using the above config, Prettier will not format TypeScript code on paste or save but it will format code on paste or save for any other language that it understands.

When working with other developers as a team, it can be challenging to keep the formatting of the code clean constantly. Not everyone will want to use the Prettier VS Code extension. Not everyone will want to use VS Code!

How do we ensure that any code that gets commited and pushed to source control is properly formatted first?

Read the next post, "Enforcing Coding Conventions with Husky Pre-commit Hooks".

Formatting using an filesystem watcher

If you really don't like VS Code or there are too many people on your team not using it, there's another option to tighten the feedback loop of formatting code as you're writing it.

The Prettier docsc suggest using a package called onchange in order to watch the filesystem for when changes are made to your source code, then run the Prettier CLI tool against any changed files.

Here's how that works.

Install onchange.

npm install --save-dev onchange

Then, add this script to your package.json, making sure to change the watched directory if you keep your source code in a location different the src folder.

package.json
"scripts": {
  "prettier-watch": "onchange 'src/**/*.ts' -- prettier --write {{changed}}"  }

Opening a new console, running prettier-watch, and editing code within the src folder will give us the same observable outcome as if we set Prettier up with VS Code or not.

Manually formatting a single file

On MacOS, if I've installed the VS Code extension, I can format the current file by typing SHIFT + OPTION + F.

This might be different for you. You can see what the command is by typing COMMAND + SHIFT + P and entering "format".

The command to format the current file in VS Code is shown here for me.

Configuring Prettier to work with ESLint

Here's where the real magic happens.

As a reminder, you'll want to have completed the previous ESLint tutorial first.

With ESLint and Prettier already installed, install these two packages as well.

npm install --save-dev eslint-config-prettier eslint-plugin-prettier
  • eslint-config-prettier: Turns off all ESLint rules that have the potential to interfere with Prettier rules.
  • eslint-plugin-prettier: Turns Prettier rules into ESLint rules.

Lastly, we need to make an adjustment to the .eslintrc.

.eslintrc
{
  "root": true,
  "parser": "@typescript-eslint/parser",
  "plugins": [
    "@typescript-eslint",
    "prettier"  ],
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended",
    "prettier"  ],
  "rules": {
    "no-console": 1,       // Means warning
    "prettier/prettier": 2 // Means error  }
}

If you're not interested in using the recommended plugins, the most basic .eslintrc with Prettier enabled looks like this:

.eslintrc
{
  "extends": ["prettier"],
  "plugins": ["prettier"],
  "rules": {
    "prettier/prettier": 2 // Means error
  }
}

Running npm run lint (from the previous article) lints the files and tells us all of the unformatted lines in addition to any code convention violations we've protected against through ESLint rules.

typescript-starter@1.0.0 lint /simple-typescript-starter
> eslint . --ext .ts

simple-typescript-starter/src/index.ts
  1:1  error    Replace `⏎console.log('Hello·world!')` with `console.log('Hello·world!');`  prettier/prettier  2:1  warning  Unexpected console statement                                                 no-console

✖ 2 problems (1 error, 1 warning)
  1 error and 0 warnings potentially fixable with the `--fix` option.

That's it! Run npm run prettier-format to format everything.

Conclusion

You can find the code for this post here.

We're pretty lucky that tooling has advanced to the point where enforcing coding conventions and formatting code isn't something we have to personally concern ourselves with anymore.

Even if you're not using VS Code, you should be able to add some basic formatting to your code using the steps outlined in this post.

After that, the next step is to enforce your conventions and formatting within your team by using a tool like Husky.

Read the final post in this series titled "Enforcing Coding Conventions with Husky Pre-commit Hooks".



Discussion

Liked this? Sing it loud and proud 👨‍🎤.



Stay in touch!



About the author

Khalil Stemmler,
Software Essentialist ⚡

I'm Khalil. I turn code-first developers into confident crafters without having to buy, read & digest hundreds of complex programming books. Using Software Essentialism, my philosophy of software design, I coach developers through boredom, impostor syndrome, and a lack of direction to master software design and architecture. Mastery though, is not the end goal. It is merely a step towards your Inward Pull.



View more in Tooling



You may also enjoy...

A few more related articles

How to Test Code Coupled to APIs or Databases
In the real-world, there's more to test than pure functions and React components. We have entire bodies of code that rely on datab...
How to Mock without Providing an Implementation in TypeScript
Having to provide an implementation everytime you create a test double leads to brittle tests. In this post, we learn how to creat...
Use DTOs to Enforce a Layer of Indirection | Node.js w/ TypeScript
DTOs help you create a more stable RESTful API; they protect your API clients from changes made on the server.
Make Illegal States Unrepresentable! - Domain-Driven Design w/ TypeScript
By using TypeScript's static type system, not only can we enforce (typically challenging things like) business rules and error sta...