Como Usar ESLint com TypeScript: typescript-eslint e Regras Avançadas em Produção Já leu

Introdução ao ESLint com TypeScript ESLint é uma ferramenta de análise estática de código que identifica padrões problemáticos em JavaScript e TypeScript. Quando você trabalha com TypeScript, o ESLint sozinho não compreende características específicas da linguagem como tipos, interfaces e decoradores. É neste ponto que entra o typescript-eslint, um projeto que integra o TypeScript compiler ao ESLint, permitindo que regras sejam criadas e executadas com total consciência dos tipos. O typescript-eslint não é apenas um parser melhorado. Ele fornece um ambiente completo onde o ESLint pode acessar a árvore de sintaxe abstrata (AST) gerada pelo TypeScript, incluindo informações semânticas que o JavaScript puro não possui. Isso permite criar regras muito mais sofisticadas, capazes de detectar erros que um simples linter tradicional jamais encontraria. Configuração Inicial do typescript-eslint Instalação e Setup Básico Para começar, você precisa instalar o typescript-eslint e suas dependências. O processo é direto e segue um padrão bem definido: Após a instalação, crie ou atualize seu arquivo na

Introdução ao ESLint com TypeScript

ESLint é uma ferramenta de análise estática de código que identifica padrões problemáticos em JavaScript e TypeScript. Quando você trabalha com TypeScript, o ESLint sozinho não compreende características específicas da linguagem como tipos, interfaces e decoradores. É neste ponto que entra o typescript-eslint, um projeto que integra o TypeScript compiler ao ESLint, permitindo que regras sejam criadas e executadas com total consciência dos tipos.

O typescript-eslint não é apenas um parser melhorado. Ele fornece um ambiente completo onde o ESLint pode acessar a árvore de sintaxe abstrata (AST) gerada pelo TypeScript, incluindo informações semânticas que o JavaScript puro não possui. Isso permite criar regras muito mais sofisticadas, capazes de detectar erros que um simples linter tradicional jamais encontraria.

Configuração Inicial do typescript-eslint

Instalação e Setup Básico

Para começar, você precisa instalar o typescript-eslint e suas dependências. O processo é direto e segue um padrão bem definido:

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

Após a instalação, crie ou atualize seu arquivo .eslintrc.json na raiz do projeto:

{
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": 2020,
    "sourceType": "module",
    "project": "./tsconfig.json"
  },
  "plugins": ["@typescript-eslint"],
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:@typescript-eslint/recommended-requiring-type-checking"
  ],
  "rules": {}
}

A opção project é fundamental. Ela aponta para seu tsconfig.json, permitindo que o ESLint acesse informações de tipo do TypeScript. Sem isso, muitas regras avançadas não funcionarão adequadamente.

Configurando o tsconfig.json

Seu tsconfig.json deve estar corretamente configurado para que o typescript-eslint extraia as informações necessárias:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"],
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "declaration": true,
    "outDir": "./dist"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

A flag strict deve estar ativa para aproveitar ao máximo as capacidades do TypeScript e do typescript-eslint. Isso garante que você esteja escrevendo código type-safe desde o início.

Regras Avançadas do typescript-eslint

Entendendo Regras com Consciência de Tipos

As regras mais poderosas do typescript-eslint são aquelas que utilizam informações de tipo para tomar decisões. Diferentemente de regras sintáticas simples, essas regras conseguem entender o significado do código. Vamos explorar algumas das mais úteis:

@typescript-eslint/no-unsafe-argument — Previne passar argumentos com tipo any para funções:

// ❌ ERRADO
function processUser(id: number): void {
  const data: any = fetchData();
  saveToDatabase(data); // ESLint alerta aqui
}

// ✅ CORRETO
interface User {
  id: number;
  name: string;
}

function processUser(id: number): void {
  const data: User = fetchData();
  saveToDatabase(data); // Seguro, tipo conhecido
}

@typescript-eslint/no-floating-promises — Detecta Promises que não são aguardadas:

// ❌ ERRADO
function loadData(): void {
  fetchUserData(); // Promise criada mas nunca aguardada
}

async function loadData(): Promise<void> {
  await fetchUserData(); // ✅ CORRETO
}

// Ou explicitamente ignora a Promise
function loadData(): void {
  void fetchUserData(); // ✅ CORRETO - Intent explícito
}

@typescript-eslint/explicit-function-return-types — Força declaração explícita do tipo de retorno:

// ❌ ERRADO
function calculateTotal(items: number[]) {
  return items.reduce((sum, item) => sum + item, 0);
}

// ✅ CORRETO
function calculateTotal(items: number[]): number {
  return items.reduce((sum, item) => sum + item, 0);
}

Regras de Null Safety

O TypeScript foi feito para prevenir erros de null e undefined. As regras abaixo reforçam essa segurança:

@typescript-eslint/strict-boolean-expressions — Garante que expressões booleanas sejam realmente booleanas:

// ❌ ERRADO
const user: { name?: string } = {};
if (user.name) { // Pode ser undefined, o que é truthy?
  console.log(user.name);
}

// ✅ CORRETO
if (user.name !== undefined && user.name !== "") {
  console.log(user.name);
}

// Ou com optional chaining
if (user.name?.length) {
  console.log(user.name);
}

@typescript-eslint/prefer-nullish-coalescing — Prefere o operador nullish coalescing (??) ao invés de ||:

// ❌ ERRADO - Falha com valores falsy legítimos
const port = process.env.PORT || 3000; // Se PORT for "0", usa 3000

// ✅ CORRETO - Só usa padrão se null ou undefined
const port = process.env.PORT ?? 3000; // "0" é mantido

Regras de Naming e Convenção

Convenções consistentes melhoram a legibilidade. O typescript-eslint fornece regras poderosas para isso:

// Configuração de regra
"@typescript-eslint/naming-convention": [
  "error",
  {
    "selector": "variable",
    "format": ["camelCase"],
    "leadingUnderscore": "allow"
  },
  {
    "selector": "typeLike",
    "format": ["PascalCase"]
  }
]

// ❌ ERRADO
const user_name = "John"; // snake_case em variável
type user = { name: string }; // lowercase em tipo

// ✅ CORRETO
const userName = "John";
type User = { name: string };

Configurações Práticas e Otimizações

Criando um Arquivo .eslintrc Profissional

A configuração abaixo representa um setup profissional, balanceado entre segurança e praticidade:

{
  "root": true,
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": 2021,
    "sourceType": "module",
    "project": ["./tsconfig.json"],
    "tsconfigRootDir": "."
  },
  "env": {
    "es2021": true,
    "node": true
  },
  "plugins": ["@typescript-eslint"],
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:@typescript-eslint/recommended-requiring-type-checking"
  ],
  "rules": {
    "@typescript-eslint/explicit-function-return-types": [
      "warn",
      {
        "allowExpressions": true,
        "allowTypedFunctionExpressions": true
      }
    ],
    "@typescript-eslint/no-unsafe-argument": "error",
    "@typescript-eslint/no-unsafe-assignment": "error",
    "@typescript-eslint/no-floating-promises": "error",
    "@typescript-eslint/no-unused-vars": [
      "error",
      {
        "argsIgnorePattern": "^_",
        "varsIgnorePattern": "^_"
      }
    ],
    "@typescript-eslint/strict-boolean-expressions": "warn",
    "@typescript-eslint/prefer-nullish-coalescing": "warn",
    "@typescript-eslint/prefer-optional-chain": "warn",
    "no-console": "warn"
  },
  "overrides": [
    {
      "files": ["*.spec.ts", "*.test.ts"],
      "rules": {
        "@typescript-eslint/explicit-function-return-types": "off"
      }
    }
  ]
}

Integrando no Package.json e Workflow

Adicione scripts para facilitar a execução do ESLint:

{
  "scripts": {
    "lint": "eslint src --ext .ts,.tsx",
    "lint:fix": "eslint src --ext .ts,.tsx --fix",
    "type-check": "tsc --noEmit",
    "validate": "npm run type-check && npm run lint"
  }
}

O script validate executa tanto o type-check do TypeScript quanto o lint, garantindo qualidade máxima antes de fazer commit.

Performance: Lidando com Grandes Projetos

Em projetos grandes, o typescript-eslint pode ficar lento porque recompila o projeto a cada execução. Existem estratégias para otimizar:

# Use cache para evitar reprocessar arquivos inalterados
eslint src --ext .ts,.tsx --cache --cache-location .eslintcache

Se ainda assim ficar lento, considere não usar project para todas as regras. Você pode criar uma configuração separada para CI/CD que roda com type-checking, e outra local mais rápida:

{
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended"
  ]
}

Esta configuração mais leve roda sem project, sendo muito mais rápida para feedback durante desenvolvimento.

Conclusão

Dominar o typescript-eslint transforma sua abordagem ao desenvolvimento TypeScript. Três pontos-chave que consolidam esse aprendizado:

  1. Tipagem e Segurança — O typescript-eslint não é apenas um linter, é um protetor que usa informações de tipo para prevenir classes inteiras de bugs em tempo de desenvolvimento, não em produção.

  2. Configuração Inteligente — Uma configuração bem pensada, com overrides para testes e balanceamento entre strictness e produtividade, é fundamental. Não copie configurações cegamente; entenda cada regra que você ativa.

  3. Integração no Workflow — ESLint é mais valioso quando integrado ao editor, pré-commit hooks e CI/CD. Um desenvolvedor que vê erros em tempo real é um desenvolvedor mais produtivo.

Referências


Artigos relacionados