DEV Community

Babette Landmesser
Babette Landmesser

Posted on

Migrate Angular 8 from TSLint to ESLint

At the end of 2019, TSLint — which is shipped with Angular core — will be deprecated. In advance, I switched from TSLint to ESLint to make migration easier and without being dependent on Angular releases.

In my daily routine as a frontend and Angular developer, I am confronted with code linting in TypeScript all the time. The Angular framework works with TypeScript and therefore added the TSLint package from palantir as a dependency. Also, Angular ships with basic TypeScript linting and serves CLI commands for checking code styles.

Sadly, palantir announced in February 2019 that the support for TSLint will be stopped at the end of the year. As a developer heavily relying on the linting and code style checks, I asked myself how to continue.

Unfortunately, Angular does not really seem to care about this. Issues for comments on the change were raised by users but not answered properly. It still seems that Angular has no real plan for migrating to ESLint — although they plan to release Angular 9 in fall 2019.

Additionally it’s quite difficult to find any help or existing articles.

Nevertheless, I feel it’s important to be prepared for unsupported TSLint so I started the migration for my Angular app on my own. In the following I will show you what I did, which conflicts I was confronted with and how I finally made ESLint work for TypeScript in an Angular environment.

Install dependencies

First of all, install the basic packages for eslint and typescript-eslint.

$ npm i -D eslint @typescript-eslint/{eslint-plugin,parser,eslint-plugin-tslint,tslint}

WARNING: I am using eslint 5.16.0 and tslint 5.18.0. With the newer version of eslint, I didn’t get the app working so far because the use of ecmaVersion has changed during this update.

@typescript-eslint is a package for extending the usual ESLint with TypeScript features, mainly of course variable types.
And also, TypeScript is parsed in a different way than ECMAScript, we also need a special parser for our linting.
In addition, @typescript-eslint/eslint-plugin adds some very specific TypeScript rules to the set of recommended ES rules, such as checking for setting the types to variables.

Basic configuration

In the next step — be aware to have tslint 5.18.0, otherwise this step is not possible — I used a package to convert my TSLint file automatically to a best possible ESLint file:

$ npx tslint-to-eslint-config

This script created a basic .eslintrc.js file with the rules it automatically detected from TSLint. As assumed, it didn’t match the complete set of TSLint rules. The reason for this is mainly the difference of the abstract syntax tree (AST) which is very different. But I won’t go into that. You can read more about AST in JavaScript here or here on TypeScript.

The basic structure of my generated eslint.js file looks like this

module.exports = {
    "env": {
        "browser": true,
        "node": true
    },
    "extends": [],
    "rules": ...,
    "globals": {},
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "project": "tsconfig.json",
        "sourceType": "module"
    },
    "plugins": [
        "@typescript-eslint",
        "@typescript-eslint/tslint"
    ],
    "settings": {}
};

So the plugin already detected how TSLint was configured and added the parser and its options, it added the environments and of course the needed plugin for TypeScript with ESLint. In the rules object, it added the rules which it automatically detected and for which the tool was able to “translate” it into ESLint or the TypeScript plugin. In addition, it pasted all rules from previous TSLint file. You can compare the TSLint and the first draft of ESLint files here:
ESLint after first generation, previous TSLint.

The most important part of the ESLint configuration in a previous TSLint project is the parser. As I mentioned before, TypeScript has a different AST than ECMAScript. ESLint obviously ships with an ES parser and we need to add our custom TypeScript parser. The use of the config plugin cared about this step already. It also preconfigured the linter to check on the already existing tsconfig.json. Most important, it added the sourceType “module” to the parserOption which defines the code to be written in ECMAScript modules instead of default script syntax.

In “env” we are able to define global variables which come with predefined parts, such as browser (uses global browser variables) or common node variables. There’s a list of further environments to be added.

Executing ESLint

As a next step, I prepared my package.json file to add an ESLint command in “scripts” section.

"eslint": "eslint 'src/**/*.ts'"

Now I was able to run

$ npm run eslint

and saw the shocking results. I got a ton of errors. The most common error I got is “ImportDeclaration should appear when the mode is ES6 and in the module context”. Ok, I was pretty stuck. I had no idea what this means. I didn’t want my code to be checked on ES6 because I need to support for example IE 11 and I need the target to be ES5. Secondly, my current setup seems to be fine too, I didn’t see why I should add an ImportDeclaration everytime.

So I started my research on this topic and I found the magic of parserOption “ecmaVersion”. If the sourceType is set to “module”, it automatically expected an ecmaVersion of 2015 or ES6. So I had to set it to 5 explicitly.

After that nearly all errors were gone.

Final adjustments

I can now start adding my own rules but this would be a lot of work. Not mentioning the maintenance of these rules. I decided to use ESLint with extending “@typescript-eslint/eslint-recommended” and “@typescript-eslint/recommended”. Again, I got a lot of errors, for example the use of “any” as a type was not allowed.

Now I was able to start adding my custom rules. ESLint allows three values for rules. “Off” or 0 for a rule that doesn’t need to match, “warn” or 1 for a rule that should match but is not a requirement and “error” or 2 for a rule that has to be applied. Pretty easy.
Checking my TSLint file and searching for matching ESLint or typescript-eslint rules, I added a set of rules to match. You find the final eslint file here.

Finally my ESLint checks on rules I want to apply and I have to improve some lines of code but all in all I am pretty set up for a future without TSLint.

One last word though: An additional check for applying Angular specific TypeScript rules for checking Components and Pipes are not yet applied.

Latest comments (1)

Collapse
 
blakerhodes profile image
Blake Rhodes

Thank you for this article it was very useful for this migration we have been putting off!