Valentia Island, Ireland (2023)

Valentia Island, Ireland (2023)

How to use eslint with Vue and TypeScript

(and pre-commit).

(Updated )

If you’re reading this, I’m assuming that you already have some sort of passing familiarity with eslint and some kind of pre-commit git hooks framework.

If not, here’s a super quick overview.

Lint is the computer science term for a static code analysis tool used to flag programming errors, bugs, stylistic errors and suspicious constructs.
Wikipedia

A linter is a program that runs through our code and checks for any errors or formatting issues. eslint is a package specifically for linting JavaScript code. When you run it with a framework like pre-commit, you can ensure that any new code adheres to your linting rules whenever you git commit.

However, there’s a little bit of tinkering to do to get eslint to work for both TypeScript and Vue.

There’s good documentation about using eslint with Vue. And there’s good documentation about using eslint with TypeScript. But there’s less documentation about using both of them together. That’s what this article is going to address.

Table of Contents:

The Answer

These are the 2 things I set up in my projects to get eslint to work with Vue and TypeScript:

  1. Set up this eslintConfig in your package.json:
package.json
"eslintConfig": {
"root": true,
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser"
},
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:vue/vue3-recommended",
"eslint-config-prettier",
"prettier"
],
"env": {
"browser": true,
"node": true,
"es2017": true
}
}
  1. Set up this eslint hook in .pre-commit-config.yml.
.pre-commit-config.yml
repos:
...
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.34.0
hooks:
- id: eslint
files: \.(js|jsx|ts|tsx|vue)$
args: ["--fix"]
additional_dependencies:
- '@typescript-eslint/[email protected]'
- '@typescript-eslint/[email protected]'

But what does it all mean?

Rather than just copy/pasting and hoping for the best, I think it’s important for us to understand what’s really going on in all of the config files that we use. So let’s go through these two configs line by line and see how they work.

We’ll start with pre-commit-config.yml.

pre-commit-config.yml, line by line

.pre-commit-config.yml
repos:
...
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.34.0
hooks:
- id: eslint
files: \.(js|jsx|ts|tsx|vue)$
args: ["--fix"]
additional_dependencies:
- '@typescript-eslint/[email protected]'
- '@typescript-eslint/[email protected]'

For the package we want to install (eslint) we specify a mirrored repo that’s designed to work with pre-commit. Pre-commit itself hosts a lot of the mirrors you’ll need, but not all of them. For instance, the pre-commit stylelint mirror is hosted by Thibaud from Wagtail.


.pre-commit-config.yml
repos:
...
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.34.0
hooks:
- id: eslint
files: \.(js|jsx|ts|tsx|vue)$
args: ["--fix"]
additional_dependencies:
- '@typescript-eslint/[email protected]'
- '@typescript-eslint/[email protected]'

I’m telling it to use version 8.34.0 which, as of publication time, is already out of date. You’ll probably want to update this.


.pre-commit-config.yml
repos:
...
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.34.0
hooks:
- id: eslint
files: \.(js|jsx|ts|tsx|vue)$
args: ["--fix"]
additional_dependencies:
- '@typescript-eslint/[email protected]'
- '@typescript-eslint/[email protected]'

This is telling pre-commit to run this hook on any changed file that ends with any of these extensions. Feel free to adjust to whatever’s appropriate for your own personal project.


.pre-commit-config.yml
repos:
...
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.34.0
hooks:
- id: eslint
files: \.(js|jsx|ts|tsx|vue)$
args: ["--fix"]
additional_dependencies:
- '@typescript-eslint/[email protected]'
- '@typescript-eslint/[email protected]'

Passing an optional --fix argument to eslint to tell it to automatically fix whatever it can.


.pre-commit-config.yml
repos:
...
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.34.0
hooks:
- id: eslint
files: \.(js|jsx|ts|tsx|vue)$
args: ["--fix"]
additional_dependencies:
- '@typescript-eslint/[email protected]'
- '@typescript-eslint/[email protected]'

The recommended way to use pre-commit is to have it manage all of its dependencies in its own virtual environment. Which works for me if it means less clutter in my package.json devDependencies.

But it also means that any plugins that we’ve specified in our eslintConfig must be included in our pre-commit hook additional_dependencies. Again, you do not install these yourself with npm install -D; when you add them to additional_dependencies pre-commit itself will install them.

Every dependency that we list is one that we’re using in our eslintConfig. Let’s take a look at that now.

eslintConfig, line by line

package.json
"eslintConfig": {
"root": true,
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser"
},
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:vue/vue3-recommended",
"eslint-config-prettier",
"prettier"
],
"env": {
"browser": true,
"node": true,
"es2017": true
}
}

For the "parser" settings, I’m just following the minimal recommendations of vue-eslint-parser.


package.json
"eslintConfig": {
"root": true,
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser"
},
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:vue/vue3-recommended",
"eslint-config-prettier",
"prettier"
],
"env": {
"browser": true,
"node": true,
"es2017": true
}
}

I add the “@typescript-eslint” plugin, following the typescript-eslint minimal configuration.


package.json
"eslintConfig": {
"root": true,
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser"
},
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:vue/vue3-recommended",
"eslint-config-prettier",
"prettier"
],
"env": {
"browser": true,
"node": true,
"es2017": true
}
}

We can “extend” other eslint configurations rather than writing out all of our own rules from scratch.

  • “eslint:recommended”

We start with eslint’s recommended configs.

  • “plugin:@typescript-eslint/eslint-recommended”
  • “plugin:@typescript-eslint/recommended”

Add the recommended configs from typescript-eslint.

  • “plugin:vue/vue3-recommended”

Add vue’s recommended configs. There are other configs from this plugin that you could use in addition.

  • “eslint-config-prettier”

This turns off all the rules that would otherwise be taken care of by my prettier rules. This prevents the two hooks from forever writing over each other. It was a lifesaver when I discovered this. You can read the docs here.

  • “prettier”

Runs prettier. Which handles slightly different concerns than eslint. Maybe there’ll be an article about that someday.


package.json
"eslintConfig": {
"root": true,
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser"
},
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:vue/vue3-recommended",
"eslint-config-prettier",
"prettier"
],
"env": {
"browser": true,
"node": true,
"es2017": true
}
}

The env section tells eslint which environments my files should be allowed to run in. Adding "browser" allows my files to use global vairables like window. "node" allows node global variables.

"es2017" allows us to use any JavaScript features up to version ECMAScript 2017. This will also set the eslintConfig’s parserOptions.ecmaVersion to 8. If you want to use even newer JavaScript features, you can set a later version.

To other way to do this instead of specifying an "env" here, is to instead set the parserOptions.ecmaVersion. You could even set it to the "latest" version, but I prefer to set my version explicitly. That way I can keep it aligned with my tsconfig.json compilerOptions.lib target.

Note: the "ecmaVersion" or "env" you specify is only for linting. It doesn’t impact what version of JavaScript that your built code eventually compiles to. That’s something you’ll set with a build tool like vite or webpack.


Congratulations! You now know as much as I do about using eslint with Vue and TypeScript. Good luck with everything.

🔆

Further Reading

  • pre-commit docs: This covers all the basics of setting up pre-commit that aren’t specific to this usecase.