A couple of months ago Typescript team revealed that they were formally adopting eslint as the linter for Typescript, and that they were actively working to improve compatibility between eslint and typescript. What you might not know is, you can use eslint with Typescript today! Read more to see how to set up eslint on your typescript project.

Vanilla Typescript Project

First of all, we need to install some dependencies:

$ npm install --save-dev eslint @typescript-eslint/parser \
    @typescript-eslint/eslint-plugin

@typescript-eslint/parser is a parser for eslint that allows eslint to parse and understand typescript files. @typescript-eslint/eslint-plugin is an eslint plugin with some typescript specific rules.

Then you need to create your .eslintrc.js file:

module.exports = {
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "project": "./tsconfig.json"
    },
    "extends": ["plugin:@typescript-eslint/recommended"],
};

And then, if your source is in the “src” folder, you can run eslint with:

$ npx eslint --ext .ts src

React

If you are using react in your project, you should also npm install --save-dev eslint-plugin-react, and then add “plugin:react/recommended” to your extends section. I also highly recommed “eslint-plugin-jsx-a11y”:

module.exports = {
    parser: '@typescript-eslint/parser',
    parserOptions: {
        project: './tsconfig.json',
    },
    settings: {
        react: {
            version: 'detect',
        },
    },
    extends: [
        'plugin:@typescript-eslint/recommended',
        'plugin:react/recommended',
        'plugin:jsx-a11y/recommended',
    ]
};

To run eslint against both .ts and .tsx files:

$ npx eslint --ext .ts --ext .tsx src

Prettier

If you use the prettier code formatter, you’ll want to manually disable all the code formatting rules, or else install eslint-config-prettier, and add the following to your .eslintrc.js file:

    "extends": [
        "plugin:@typescript-eslint/recommended"
        "prettier",
        "prettier/@typescript-eslint"
    ]

VisualStudio Code

If you’re using VisualStudio Code, you’ll need to enable typescript support in the eslint plugin in the eslint.validate setting. You can do this by creating a file called “.vscode/settings.json” in your project folder which contains:

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

Migrating from tslint

If you want to disable rules for specific files or lines, the format of the pragmas in eslint are slightly different from tslint. In tslint, you might put something like this at the top of the file:

/*t slint:disable no-unused-vars */

The eslint equivalent would be:

/* eslint-disable @typescript-eslint/no-unused-vars */

Similarly, to disable tslint for a single line you might do:

// tslint:disable-next-line no-for-in-array

Where with eslint you would od:

// eslint-disable-next-line @typescript-eslint/no-for-in-array

If you’re using this on an existing projects, you may want to disable some of the “recommended” rules if they’re causing too much noise, which you can do in your eslint config file. Here we also disable some rules specifically for files in “/test”:

module.exports = {
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "project": "./tsconfig.json"
    },
    "extends": ["plugin:@typescript-eslint/recommended"],
    "rules": {
        "@typescript-eslint/explicit-function-return-type": "off",
        "@typescript-eslint/no-explicit-any": "off",
        // Typescript's unused variable checking is better, IMHO.
        "@typescript-eslint/no-unused-vars": "off",
    },
    overrides: [
        {
            // Disable some rules that we ruthlessly abuse in unit tests.
            files: ['test/**/*.ts', 'test/**/*.tsx'],
            rules: {
                '@typescript-eslint/no-non-null-assertion': 'off',
                '@typescript-eslint/no-object-literal-type-assertion': 'off',
            },
        },
    ],
};

Mixed JS and TS Projects

If you’ve used eslint before, one thing you may have noticed is that, instead of using eslint’s built in “no-unused-vars”, the typescript-eslint plugin defines it’s own “@typescript-eslint/no-unused-vars”. eslint works by generating something called an “abstract syntax tree” or AST - a kind of formal description of your source code. When you use the “@typescript-eslint/parser” plugin, the AST includes some extra information about types and other language features that are not available in standard javascript. This extra information may trip up some eslint plugins, and some of the built in rules.

We’ll probably see more and more plugins handle the typescript corner cases in future, but for now you effectively need one configuration for js files, and another for ts files. Fortunately, eslint has a built in feature called “overrides” which should make it easy to specify different rules for different files… unfortunately at the moment you are not allowed to use an extends inside an overrides, which means you can’t easily bring in the recommended rule set. There’s an issue for getting this fixed, and another issue looking at improving eslint’s configuration file overall.

One solution is to use different configuration files for .ts and .js files. Then you can do something like:

$ eslint --config ./.eslintrc-ts --ext ts
$ eslint --config ./.eslintrc-js --ext js

but this is hard to make work with your favorite editor. If you have entire subtrees that are typescript or javascript, another solution is to put a “.eslintrc.js” in each subtree, with different config; eslint will find the “closest” configuration file (although it will keep walking up the tree, and merge higher up configuration files with lower ones).

The solution below was suggested by @bradzacher, and uses the fact that eslint will let us use a .js config file. The basic idea is to require eslint-plugin’s recommended configuration, and manually merge it into an overrides block to get around the extends limitation:

// .eslintrc.js
const typescriptEslintRecommended = require('@typescript-eslint/eslint-plugin').configs.recommended;

module.exports = {
    "extends": [
        "eslint:recommended",
    ],

    "parserOptions": {
        "ecmaVersion": 8,
        "sourceType": "module",
        "ecmaFeatures": {
            "impliedStrict": true
        }
    },

    "env": {
        "node": true,
        "es6": true,
        "mocha": true
    },
    overrides: [
        {
            files: ['**/*.ts', '**/*.tsx'],
            parser: '@typescript-eslint/parser',
            parserOptions: {
                sourceType: 'module',
                project: './tsconfig.json',
            },
            plugins: [ '@typescript-eslint' ],
            rules: Object.assign(typescriptEslintRecommended.rules, {
                '@typescript-eslint/no-unused-vars': 'off',
            })
        }
    ],
};

This is obviously a little fragile, though.

Hopefully this helps you get set up playing with eslint on your typescript project!