The horse is dead – cry: transition from tslint to eslint

7 min

Until recently, in all projects of the front, the developers of Dodo Pizza Engineering used tslint – a useful tool that tells you when messed up in code made an inaccuracy, helps maintain the code in one style, and corrects many comments itself. But then tslint took and died. Under the cut, I’ll tell you why it happened, how to stop pouring tears over the deceased and switch to the eslint tool, and also show something very intimate.

In fact, it all started a long time ago: the last release of the tslint kernel was already in 2016. And this is the moment when it is time to start saying “last,” if someone still says “last,” because that release was really the last. On February 19, 2019, an official post was released to stop the development of tslint. In it, the development company (by the way, it’s not even Microsoft) strongly advises everyone to switch to eslint, since their efforts will now be aimed at improving TypeScript support in this linter.

One language, one stack, one community

Microsoft sees TypeScript as the main web development language, which should supplant Java / ECMA Script. Obviously, such an ambitious goal implies a single stack of tools for the entire front-end development. This should greatly simplify the migration of the large JS community to TypeScript. In addition to the guarantee of trust from Microsoft, eslint has a better architecture than tslint. For example, you can connect parsers, and there are more choice of connected rules.

Microsoft would not be itself if it were just wanted. Whatever we say about the quality of their software, they do great development tools (and, by the way, input devices). So this time they came not empty-handed, but wrote a migration plan. In accordance with this plan, the development of tslint rules has already been discontinued on August 1, 2019, and the development of tslint itself will cease on November 1, 2019. Although, to be honest, development has been discontinued a long time ago (see above for the latest release).

Here it should become obvious to the reader that it is time to switch to eslint, there is no other choice. To sweeten the pill, it is worth saying that:

  • while tslint is focused on TypeScript with a greater emphasis on the correct use of types and syntax checking, eslint covers everything that can be in the front, including the syntax of React components;
  • eslint has many more predefined rules;
  • there are rules (and plugins) that check the code at the block level (code duplication, perceived complexity, etc.);
  • there are plugins that check not code at all, but, for example, regular expressions.

In general, it looks like the transition to a new linter, which is a mainstream product, will open up a whole world of previously unseen opportunities to us. Well, let's try it!

Add eslint to the project

I’ll talk about the migration of rules below. In the meantime, set up a project to work with eslint.
If you already have a project with tslint, then first remove all packages related to tslint from it: tslint itself, tslint-react, tslint-config-prettier, etc.

Now add the eslint packages to the project (set everything as devDependencies):

  • eslint itself;
  • @ typescript-eslint / parser – engine for parsing TypeScript;
  • @ typescript-eslint / eslint-plugin – rule sets for TypeScript

Eslint minimal setup

Create the .eslintrc.json configuration file. Eslint supports many file formats for its configuration, but JSON seems the most convenient. Here's what the minimal working option looks like:

    // Project Settings
    "env": {
// Project for browser
"browser": true,
// Enable ES6 Features
"es6": true,
// Add ES2017 features
"es2017": true
    // rule sets
    "extends": [
		// Базовый набор правил eslint
		// Отключаем правила из базового набора
		// Базовые правила для TypeScript
		 // Правила TS, требующие инфо о типах
// Parsing engine
"parser": "@ typescript-eslint / parser",
"parserOptions": {
// The engine needs a TS project for rules with types
"project": "tsconfig.json",
"tsconfigRootDir": ".",
// Plugin with rule sets for TypeScript
"plugins": ["@typescript-eslint"],
"rules": {}

Section env tells eslint about your project options. In my example, this is a project for the browser (i.e. the code will work in the browser). Write for Node.JS – set node: true. The two following options indicate the dialect of the JS being tested. In general, we will check the code for TypeScript, but if your project also has code for JS, do not forget to tighten them. For ourselves, we decided that we set these parameters to the same value as target in tsconfig.json.

There is nothing controversial in the standard eslint rule sets, such as the required semicolon at the end of expressions or spaces / tabs. All rules are uniquely useful. You can see what rules and with what level are included here.

The next line you need to disable half the rules. This is necessary because they do not work with TypeScript and instead of normal operation they will throw a bunch of errors.

Then you should connect the recommended rules from TypeScript in a separate bag. Here you need to keep in mind that general syntax rules (such as banning var) will work like that.

But for rules that use TS types (for example, @ typescript-eslint / no-unnecessary-type-assertion), the TypeScript engine is needed. And the engine will need the tsconfig.json file, the path to which must be specified.

In tsconfig.json, we at Dodo Pizza Engineering usually specify exclude and throw out tests so that they do not build with the project. But for eslint to work, you must specify and include. That is, all files that need to be linted must be explicitly included in the project. Without this, eslint will swear at every file it finds: “The file is not in the project, I won’t do anything, I’ll throw a bunch of errors.” There is an option without explicitly specifying project files – set the parameter createDefaultProgram: true. This, in essence, means: "All that you find is Parsi." But developers strongly advise against doing so because of a significant drop in performance.

If you use ForkTsCheckerWebpackPlugin to process TypeScript files, then replace it with its parameters (in webpack.config.ts) tslint: true on eslint: true.

It is also worth setting up the linter launch from the command line. Before that, add this value to the scripts section in package.json:

"eslint": "eslint --cache --ext .js, .jsx, .ts, .tsx src",
"eslint: dump": "eslint --print-config ./.eslintrc.json",

The first line just starts eslint validation without building the project. The second displays the current eslint settings, which allows you to see the settings for the rule parameters.

In this version, eslint will already work in the project and even catch some shoals: redefining globals, unused variables, etc.

Setting up Visual Studio Code

After you have gone all this way, you can already start the linter from the command line. It will also be implicitly launched when building the project. But in Visual Studio Code we will not see comments from the linter. How so ?!

There is an eslint plugin for the studio (dbaeumer.vscode-eslint), it needs to be installed. After that, nothing will work anyway, nothing will be emphasized and corrected. Why? Because the plugin has a config, which says that you need to work only in JavaScript files.

This sneaky setting is not made in the UI, so you need to go into the studio settings file and manually add the languages ​​you need to the eslint.validate parameter. A complete list of languages ​​can be found in the bowels of the studio documentation. Here's what this setting looks like with us:

"eslint.validate": [

After that, restart the studio and everything will finally start working.

Now it remains to configure the rules

The project is set up. Now about the rules, because in the example above the list of rules was empty.

I must say that tslint did not stop us from messing up in formally correct code. For example, forget await. Eslint knows how to find such semantic errors and swear at them: to report that the return value is Promise, but for some reason, await is not written for it. This also includes stylistic problems of medium complexity: using a lambda or function, etc., which Prettier can no longer do.

Regarding simple rules: position of brackets, tabs vs. spaces, etc., it is believed that they should be given to Prettier or a similar package. But in the linter they should be left anyway: this is the last frontier, which is still able to stop the negligent developer of the fallen project assembly. Moreover, this line can be automated: for example, husky, allows you to start the linter automatically for each commit.

We decided not to migrate any of the tslint rule sets that we have. And create your own set from scratch.

There are predefined rule sets for eslint:

  • ESLint Recommended is a neutral set of rules that is made with the idea of ​​not spawning holivars. Only obviously necessary checks are included: unused variables, etc. All subsequent sets extend this one.
  • Google – there is already a reason for holivar: for indentation there are strictly spaces, a semicolon is required.
  • AirBnB – There are also strict style rules, including a mandatory semicolon.
  • Standart – semicolons are forbidden here, but trailing commas are also forbidden.

We did not like any of the ready-made packages. This may sound strange, but it’s important for us to switch to a new linter, avoiding stylistic wars. If we write like this everywhere (tabs, without a semicolon, trailing commas are mandatory), then let it remain so – the main thing is that it is the same in all projects.

Promised sex: its own set of rules

Honestly, to show your eslint rule set is like a girl showing boobs: there are no more secrets. I thought for a long time whether to do this. But, after consulting with fellow girls, I decided what was worth.

I'll start with the plugins we use:

  • react – checks for react component code. A basic set of rules plus ours. From the important: we drown for pure functional components;
  • react-hooks – rules from react developers about using hooks;
  • promise – checks for common errors when using Promise. It works a bit strange with TypeScript code. From the important: we try to use Promise everywhere and not use callbacks and then / catch because of better readability of the code;
  • optimize-regex is a fun plugin that provides tips for improving regular expressions. Not very useful, because regexp we have a bit. But far from all possess regexp magic. So it is useful, but there are many who do not ask;
  • sonarjs is a fire plugin with checks for code complexity and typical refactoring errors. The first is a funny thing: the plugin evaluates the perceived complexity of the code and gives advice when it is worth simplifying the code. The search for refactoring errors often also allows you to simplify the code or, at least, improve readability;
  • @ typescript-eslint – eslint rules for checking TypeScript code. And a set to disable basic rules that are not compatible with TS.

Our entire rule file is here. I note that it is not a dogma and is updated as it adapts to projects.


Leave a Reply