Configurando ESLint com typescript-eslint
1. Por que usar ESLint com TypeScript?
O TypeScript Compiler (tsc) é excelente para detectar erros de tipo, mas possui limitações significativas na identificação de más práticas de código, padrões problemáticos e violações de estilo. Enquanto tsc foca em segurança de tipos, o ESLint complementa essa análise com linting de código, detectando problemas como variáveis não utilizadas, expressões booleanas complexas e práticas inconsistentes.
A combinação typescript-eslint oferece o melhor dos dois mundos: utiliza o parser TypeScript para entender a estrutura de tipos do seu código e aplica regras de linting que consideram essas informações. Isso permite, por exemplo, detectar variáveis declaradas mas nunca usadas considerando tipos, ou sugerir substituições seguras de @ts-ignore por @ts-expect-error.
A diferença fundamental é:
- Erros de tipo (TypeScript): tsc verifica se você está passando argumentos corretos, acessando propriedades existentes, etc.
- Erros de estilo/lógica (ESLint): ESLint verifica se você está seguindo convenções, evitando padrões problemáticos, mantendo consistência no código.
2. Instalação e dependências necessárias
Para começar, instale as dependências principais:
npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
Certifique-se de usar versões compatíveis. Para projetos com TypeScript 5.x, recomenda-se:
npm install --save-dev eslint@^8.56.0 @typescript-eslint/parser@^7.0.0 @typescript-eslint/eslint-plugin@^7.0.0
Se você já possui outras dependências de linting, como eslint-plugin-react ou eslint-plugin-import, verifique a compatibilidade consultando a documentação oficial de cada plugin.
3. Configuração básica do eslint.config.js
A partir do ESLint v9, a configuração flat config (eslint.config.js) é o padrão. Abaixo está um exemplo de configuração mínima para TypeScript:
// eslint.config.js
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
{
languageOptions: {
parserOptions: {
project: ['./tsconfig.json'],
tsconfigRootDir: import.meta.dirname,
},
},
rules: {
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/explicit-function-return-type': 'warn',
},
}
);
Se você ainda usa o formato .eslintrc (legado), a configuração equivalente seria:
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"plugins": ["@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
]
}
4. Regras essenciais do typescript-eslint
Regras de tipo
@typescript-eslint/no-unnecessary-type-assertion: Detecta type assertions desnecessárias, quando o TypeScript já infere o tipo corretamente.
// Ruim
const nome: string = 'João' as string;
// Bom
const nome: string = 'João';
@typescript-eslint/prefer-ts-expect-error: Prefere@ts-expect-errora@ts-ignore, pois o primeiro gera erro se a linha subjacente não tiver erro de tipo.
// Ruim
// @ts-ignore
const x: number = 'string';
// Bom
// @ts-expect-error - Intencional: testando compatibilidade
const x: number = 'string';
Regras de boas práticas
@typescript-eslint/no-unused-vars: Versão melhorada da regra do ESLint, que entende variáveis TypeScript como tipos importados.
// Ruim
function soma(a: number, b: number) {
return a;
}
// Bom
function soma(a: number, b: number): number {
return a + b;
}
@typescript-eslint/explicit-function-return-type: Exige que funções tenham tipo de retorno explícito.
// Ruim
function getNome() {
return 'Maria';
}
// Bom
function getNome(): string {
return 'Maria';
}
Regras de consistência
@typescript-eslint/consistent-type-definitions: Prefere interfaces a type aliases (ou vice-versa, conforme configurado).
// Ruim (se configurado para preferir interfaces)
type Usuario = {
nome: string;
};
// Bom
interface Usuario {
nome: string;
}
@typescript-eslint/consistent-type-imports: Garante que imports de tipos usemimport type.
// Ruim
import { Usuario } from './types';
// Bom
import type { Usuario } from './types';
5. Integração com outras configurações e plugins
Combinando com React
Para projetos React, instale o plugin específico:
npm install --save-dev eslint-plugin-react eslint-plugin-react-hooks
E configure:
// eslint.config.js
import reactPlugin from 'eslint-plugin-react';
import reactHooksPlugin from 'eslint-plugin-react-hooks';
export default tseslint.config(
// ... outras configurações
{
plugins: {
react: reactPlugin,
'react-hooks': reactHooksPlugin,
},
rules: {
'react/jsx-uses-react': 'error',
'react/jsx-uses-vars': 'error',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
},
}
);
Combinando com import sorting
npm install --save-dev eslint-plugin-import
rules: {
'import/order': ['error', {
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
'newlines-between': 'always',
}],
}
Evitando conflitos com Prettier
npm install --save-dev eslint-config-prettier
No formato flat config:
import prettierConfig from 'eslint-config-prettier';
export default tseslint.config(
// ... outras configurações
prettierConfig,
);
6. Configurando scripts e automação
Adicione ao package.json:
{
"scripts": {
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix",
"lint:watch": "esw src/ --watch"
}
}
Para integração com VS Code, instale a extensão ESLint e adicione ao settings.json:
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.validate": ["typescript", "typescriptreact"]
}
Para CI/CD com GitHub Actions:
name: Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm run lint
7. Resolução de problemas comuns
Erro "Parsing error: Cannot read file 'tsconfig.json'"
Verifique se o tsconfig.json está no diretório raiz do projeto e se o parserOptions.project aponta para o caminho correto. Se usar monorepo, especifique o caminho relativo:
parserOptions: {
project: ['./packages/*/tsconfig.json'],
}
Conflitos entre regras TypeScript e ESLint
Desative regras do ESLint que conflitam com as versões TypeScript:
rules: {
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'error',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': 'error',
}
Ignorando arquivos específicos
No flat config, use a propriedade ignores:
export default tseslint.config(
{
ignores: ['dist/', 'node_modules/', '*.config.js'],
},
// ... outras configurações
);
Ou crie um arquivo .eslintignore (para configuração legada):
dist/
node_modules/
*.config.js
Com essa configuração, seu projeto TypeScript estará protegido contra más práticas comuns, mantendo consistência e qualidade de código. Lembre-se de revisar periodicamente as regras conforme seu projeto evolui, ajustando níveis de severidade e adicionando novas regras quando necessário.
Referências
- Documentação oficial do typescript-eslint — Guia completo de instalação, configuração e todas as regras disponíveis para TypeScript.
- ESLint Flat Config (Configuration Migration Guide) — Documentação oficial sobre a migração para o formato flat config do ESLint v9.
- TypeScript ESLint Plugin: Regras Recomendadas — Lista completa de regras com exemplos de código correto e incorreto.
- Configurando ESLint com TypeScript e React — Tutorial prático de configuração integrada com React e React Hooks.
- ESLint + Prettier: Como evitar conflitos — Guia oficial do Prettier sobre integração com ESLint e uso do eslint-config-prettier.
- GitHub Actions: Linting em CI/CD — Documentação do GitHub para configurar linting automático em pipelines.
- Resolvendo problemas comuns do typescript-eslint — Comunidade Stack Overflow com soluções para erros frequentes de configuração.