Introduction
TypeScript has become the standard for adding type safety to JavaScript projects, but maintaining code quality requires more than just types. Linting and formatting tools are essential for ensuring consistent code style and catching potential issues before they become problems.
For years, TSLint was the go-to linting solution for TypeScript projects. However, in 2019, the TSLint team announced that they would be deprecating TSLint in favor of supporting TypeScript in ESLint, leading to the creation of the @typescript-eslint project.
This guide will walk you through the process of migrating from TSLint to ESLint in your TypeScript projects, explaining the benefits, steps, and best practices to ensure a smooth transition.
Why Migrate from TSLint to ESLint?
There are several compelling reasons to migrate from TSLint to ESLint:
- Official recommendation: The TypeScript team now recommends ESLint over TSLint
- Active maintenance: TSLint is deprecated and no longer receiving updates
- Unified tooling: Use the same linting tool for both JavaScript and TypeScript
- Larger ecosystem: ESLint has a more extensive plugin ecosystem and community support
- Better performance: ESLint generally performs better, especially on larger codebases
- More flexible configuration: ESLint offers more granular control over rules and configurations
Benefits of ESLint for TypeScript
Migrating to ESLint for your TypeScript projects offers numerous advantages:
Benefit | Description |
---|---|
Improved rule coverage | Access to both JavaScript and TypeScript-specific rules |
Better integration | Seamless integration with Prettier and other tools |
Custom rules | Easier creation of custom rules and plugins |
Consistent experience | Same linting experience across JavaScript and TypeScript files |
Future-proof | Ongoing development and community support |
Prerequisites
Before starting the migration process, ensure you have:
- A TypeScript project currently using TSLint
- Node.js (version 12 or later recommended)
- npm or yarn package manager
- Basic familiarity with linting concepts
It's also helpful to have your current TSLint configuration handy, as you'll need to map these rules to their ESLint equivalents.
Migration Steps
Let's break down the migration process into manageable steps:
Step 1: Remove TSLint
First, remove TSLint and its dependencies:
# Using npm
npm uninstall tslint typescript-tslint-plugin
# Using yarn
yarn remove tslint typescript-tslint-plugin
Also, remove any TSLint configuration files, such as tslint.json and tslint.yaml.
Step 2: Install ESLint and Dependencies
Install ESLint and the TypeScript parser and plugin:
# Using npm
npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
# Using yarn
yarn add --dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
Step 3: Create ESLint Configuration
Create a new .eslintrc.js (or .eslintrc.json) file in your project root:
// .eslintrc.js
module.exports = {
parser: '@typescript-eslint/parser',
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
],
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
project: './tsconfig.json',
},
plugins: ['@typescript-eslint'],
rules: {
// Add custom rules here
},
};
Step 4: Migrate TSLint Rules to ESLint
Map your existing TSLint rules to their ESLint equivalents. The @typescript-eslint/eslint-plugin provides many rules that match TSLint functionality.
Common mappings include:
TSLint Rule | ESLint Equivalent |
---|---|
interface-name | @typescript-eslint/naming-convention |
no-unused-variable | @typescript-eslint/no-unused-vars |
no-use-before-declare | no-use-before-define |
quotemark | quotes |
trailing-comma | comma-dangle |
You can use the{" "} tslint-to-eslint-config {" "} tool to help with this migration:
npx tslint-to-eslint-config
Step 5: Update Scripts and CI/CD
Update your package.json scripts to use ESLint instead of TSLint:
{
"scripts": {
"lint": "eslint 'src/**/*.{js,ts,tsx}' --fix",
"lint:check": "eslint 'src/**/*.{js,ts,tsx}'"
}
}
Also update any CI/CD configurations to use the new ESLint commands.
Step 6: Test Your Configuration
Run ESLint to test your configuration:
npm run lint
# Or using npx directly
npx eslint src/ --ext .ts,.tsx
Fix any issues that arise and adjust your rules as needed.
ESLint Configuration
Let's explore some common ESLint configurations for TypeScript projects.
Basic Configuration
A simple configuration extends the recommended TypeScript ESLint rules:
// .eslintrc.js
module.exports = {
parser: '@typescript-eslint/parser',
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
],
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
},
plugins: ['@typescript-eslint'],
};
Advanced Configuration
For more complex projects, you might want a more comprehensive configuration:
// .eslintrc.js
module.exports = {
parser: '@typescript-eslint/parser',
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
],
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
project: './tsconfig.json',
},
plugins: ['@typescript-eslint'],
rules: {
'@typescript-eslint/explicit-function-return-type': 'error',
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-unused-vars': ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_'
}],
'no-console': 'warn',
},
};
Integrating with Prettier
Prettier works well with ESLint to handle code formatting, while ESLint handles code quality:
# Using npm
npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier
# Using yarn
yarn add --dev prettier eslint-config-prettier eslint-plugin-prettier
Update your ESLint configuration:
// .eslintrc.js
module.exports = {
parser: '@typescript-eslint/parser',
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
},
plugins: ['@typescript-eslint', 'prettier'],
rules: {
'prettier/prettier': 'error',
},
};
Create a .prettierrc.js file for Prettier configuration:
// .prettierrc.js
module.exports = {
semi: true,
trailingComma: 'all',
singleQuote: true,
printWidth: 80,
tabWidth: 2,
};
Rule Customization Examples
Here are some common rule customizations for TypeScript projects:
// .eslintrc.js
module.exports = {
// ... rest of config
rules: {
// Enforce consistent type imports
'@typescript-eslint/consistent-type-imports': ['error', {
prefer: 'type-imports'
}],
// Allow unused variables when prefixed with _
'@typescript-eslint/no-unused-vars': ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_'
}],
// Ban specific types
'@typescript-eslint/ban-types': ['error', {
types: {
Object: {
message: 'Use object instead',
fixWith: 'object',
},
Function: {
message: 'Use a specific function type instead',
},
},
}],
// Enforce naming conventions
'@typescript-eslint/naming-convention': [
'error',
{
selector: 'interface',
format: ['PascalCase'],
prefix: ['I'],
},
{
selector: 'typeAlias',
format: ['PascalCase'],
},
{
selector: 'enum',
format: ['PascalCase'],
},
],
},
};
Common TypeScript ESLint Rules
Let's explore some essential TypeScript-specific ESLint rules:
Syntax Rules
- @typescript-eslint/explicit-function-return-type:{" "} Requires explicit return types on functions
- @typescript-eslint/explicit-member-accessibility:{" "} Requires explicit accessibility modifiers on class members
- @typescript-eslint/typedef: Requires type annotations in specific locations
Best Practices Rules
- @typescript-eslint/no-explicit-any: Disallows usage of the any type
- @typescript-eslint/no-unnecessary-type-assertion:{" "} Prevents redundant type assertions
- @typescript-eslint/prefer-nullish-coalescing:{" "} Enforces using nullish coalescing over logical OR
- @typescript-eslint/prefer-optional-chain: Enforces using optional chaining over chained logical AND
Style Rules
- @typescript-eslint/naming-convention: Enforces consistent naming conventions
- @typescript-eslint/member-ordering: Enforces a consistent member ordering
- @typescript-eslint/array-type: Enforces consistent array type definition
- @typescript-eslint/consistent-type-definitions:{" "} Enforces interface or type for object type definitions
Editor Integration
Configure your editor to provide real-time ESLint feedback:
Visual Studio Code
Install the ESLint extension and add these settings to your settings.json:
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
]
}
WebStorm
WebStorm has built-in support for ESLint:
- Go to Settings/Preferences
- Navigate to Languages & Frameworks {">"} JavaScript {">"}{" "} Code Quality Tools {">"} ESLint
- Enable ESLint and configure the settings as needed
- To enable auto-fix on save, go to Tools {">"} Actions on Save and check "Run eslint --fix"
Other Editors
Most popular editors have ESLint plugins or extensions:
- Atom: Install the "linter-eslint" package
- Sublime Text: Install "SublimeLinter" and "SublimeLinter-eslint"
- Vim/Neovim: Use ALE (Asynchronous Lint Engine) or CoC (Conquer of Completion)
JetBrains IDEs (WebStorm, IntelliJ IDEA)
For JetBrains IDEs, ESLint integration is built-in:
- Go to Settings/Preferences
- Navigate to Languages & Frameworks {">"} JavaScript {">"}{" "} Code Quality Tools {">"} ESLint
- Enable ESLint and configure the settings as needed
- To enable auto-fix on save, go to Tools {">"} Actions on Save and check "Run eslint --fix"
Automating ESLint
Automating ESLint checks ensures code quality is maintained throughout your development process.
Git Hooks with Husky
Use Husky and lint-staged to run ESLint before commits:
# Using npm
npm install --save-dev husky lint-staged
# Using yarn
yarn add --dev husky lint-staged
Configure in your package.json:
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,ts,tsx}": [
"eslint --fix",
"git add"
]
}
}
For Husky v7+, the setup is slightly different:
npx husky install
npx husky add .husky/pre-commit "npx lint-staged"
And in package.json:
{
"scripts": {
"prepare": "husky install"
},
"lint-staged": {
"*.{js,ts,tsx}": "eslint --fix"
}
}
CI/CD Integration
Add ESLint checks to your CI/CD pipeline. Here's an example for GitHub Actions:
# .github/workflows/lint.yml
name: Lint
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
eslint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- run: npm ci
- run: npm run lint
TypeScript Formatting Tips
- Run ESLint before committing to catch formatting issues
- Configure VS Code to format on save for real-time feedback
- Use Prettier alongside ESLint for consistent code style
- Add ESLint to your CI/CD pipeline to enforce standards
- Keep your ESLint configuration up to date with project needs
Format Your TypeScript Code Online
Need to quickly format TypeScript code without setting up ESLint? Try our online TypeScript formatter.
Format TypeScript Code