Skip to content
Notifications
Clear all

Guide: migrating from TSLint to ESLint without breaking everything

2 Posts
2 Users
0 Reactions
1 Views
(@devops_grandad)
Estimable Member
Joined: 2 months ago
Posts: 100
Topic starter   [#20446]

Alright, let's get this straight. You're here because you've been told TSLint is deprecated and you need to move to ESLint. You're looking at a massive codebase, a mountain of custom rules, and a CI pipeline that will scream if you introduce a single new linting error. You're right to be nervous. I've seen teams try a "big bang" migration and it cripples productivity for a week. We're not doing that.

We're going to do this surgically, in stages, without breaking a single existing build. The key is to run both linters side-by-side for a while, migrating rule sets incrementally. This isn't about being trendy; it's about keeping the lights on while you swap the engine.

First, you need to install the new machinery alongside the old. Get these packages in place.

```bash
npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
```

Now, here's the critical part. Your initial `.eslintrc.js` should be set up to only catch the most egregious syntax errors, **not** to replicate your TSLint rules yet. We'll get to those later.

```javascript
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended', // Just the basics to start
],
env: {
node: true,
es6: true
}
};
```

The real workhorse is your `package.json` scripts. You will run **both** linters for a period. Your CI should run them both, but only have TSLint fail the build initially. This keeps the old guard in charge while the new one learns the ropes.

```json
"scripts": {
"lint:ts": "tslint --project tsconfig.json",
"lint:js": "eslint . --ext .ts,.tsx --max-warnings 0",
"lint": "npm run lint:ts && npm run lint:js"
}
```

Now, the migration of rules. Do **not** try to do this manually. Use the `tslint-to-eslint-config` utility. It's not perfect, but it gets you 80% there for the standard rules.

```bash
npx tslint-to-eslint-config
```

It'll spit out a config. You will need to manually review its output, especially for any custom TSLint rules you had. For those, you'll need to find equivalent ESLint plugins (like `eslint-plugin-import` for import ordering, `eslint-plugin-jsdoc` for doc rules, etc.). This is the time-consuming part. Do it one rule category at a time (e.g., all "style" rules, then all "functionality" rules).

The process for each chunk of rules is the same:
* Add the new ESLint rule or plugin to your config.
* Run `eslint --fix` on the codebase. Let it autofix what it can.
* Run both linters. ESLint will now be checking the new category.
* **Crucially:** Temporarily disable the corresponding TSLint rule category in your `tslint.json`. This silences the duplicate noise and prevents "linting fatigue."
* Commit. Your CI still passes because TSLint is still technically running (just with fewer rules), and ESLint isn't set to fail the build yet.

Repeat this until your TSLint config is effectively empty. At that point, flip the switch in CI: make ESLint failures break the build, and you can remove TSLint entirely. You've migrated without a single broken build, and your team only had to deal with incremental changes. That's how you do it in production.



   
Quote
(@andrew8)
Estimable Member
Joined: 1 week ago
Posts: 77
 

Side-by-side works, but watch your CI time. I've seen builds double when running both linters over 500k lines. Run ESLint only on changed files in the initial phase to keep feedback fast.

Your empty config is the right start. I'd add `parserOptions.project: true` from day one, though. It'll save you from scope analysis headaches later when you port the rules that need type info.


Numbers don't lie.


   
ReplyQuote