Linting de TypeScript com ESLint
Configuração flat do ESLint 10 para TypeScript: setup com typescript-eslint, lint tipado com projectService e Prettier sem conflito.
Em julho de 2026, a forma atual de fazer linting em TypeScript é usar o ESLint 10 com o pacote typescript-eslint em flat config (eslint.config.mjs) — e não a antiga configuração .eslintrc que a maioria dos resultados de busca ainda exibe.
Se você já colou uma configuração de algum tutorial de 2022 e viu o ESLint ignorá-la por completo, o motivo é esse: ela foi escrita para um sistema de configuração que não existe mais. A substituição é curta, embora a parte type-aware exija uma opção extra fácil de passar despercebida.
O ESLint 10 removeu de vez o sistema de configuração eslintrc, como o projeto já havia sinalizado em seus planos de adoção do flat config. Essa única mudança quebra praticamente todo tutorial anterior a 2024, porque o ESLint simplesmente não lê mais arquivos .eslintrc ou .eslintignore. Este guia traz uma flat config correta e pronta para copiar e colar para TypeScript, mostra como ativar as regras type-aware e integra o linting aos seus scripts, ao editor e ao CI.
Principais Conclusões
- A stack moderna é ESLint 10 mais typescript-eslint v8 em flat config;
.eslintrc/.eslintignoreestão mortos a partir do ESLint 10. - Uma configuração mínima passa
js.configs.recommendedetseslint.configs.recommendedparadefineConfig(), importado deeslint/config, em um arquivo chamadoeslint.config.js/.mjs. - Regras type-aware como
no-floating-promisesprecisam deparserOptions: { projectService: true }. UmparserOptionsvazio não as habilita. - O linting tipado pede que o TypeScript compile seu projeto antes do lint, então é mais lento; execute-o no CI e conte com o cache da IDE no editor.
- No flat config a flag
--extestá obsoleta: a seleção de arquivos vive no globfilesde cada bloco, então o script de lint é apenaseslint ..
ESLint e TypeScript fazem o mesmo trabalho?
ESLint e TypeScript são complementares, não concorrentes. Um punhado de regras do typescript-eslint de fato recorre ao type checker do TypeScript para uma leitura mais profunda do seu código, mas as duas ferramentas respondem a perguntas diferentes: o compilador do TypeScript verifica se os tipos batem, enquanto o ESLint impõe estilo e captura prováveis bugs (variáveis não utilizadas, promises soltas, padrões inseguros) em toda a base de código. Você usa os dois.
Se você está migrando do TSLint, saiba que ele está morto há anos. Seus mantenedores anunciaram em 2019 que o depreciariam em favor do typescript-eslint, e o ecossistema do ESLint se tornou o padrão para linting de TypeScript. Não há motivo para recorrer ao TSLint em um projeto novo.
Um pré-requisito antes de instalar: o ESLint 10 abandonou versões mais antigas do Node. Ele agora roda no Node.js v20.19.0 ou superior, v22.13.0 ou superior, ou v24 ou superior, sendo que v21.x e v23.x não são mais suportadas.
Como configurar o ESLint para TypeScript?
Discover how at OpenReplay.com.
Instale os quatro pacotes de que você realmente precisa:
npm i -D eslint @eslint/js typescript typescript-eslint
O utilitário typescript-eslint empacota o parser e o plugin, de modo que você não precisa conectar @typescript-eslint/parser e @typescript-eslint/eslint-plugin manualmente. Ele suporta a major atual: o intervalo de versões do ESLint documentado pelo typescript-eslint cobre ^8.57.0 || ^9.0.0 || ^10.0.0, então typescript-eslint@latest (v8.x) roda sem problemas no ESLint 10.
Crie o eslint.config.mjs (flat config, não .eslintrc):
// eslint.config.mjs
import js from '@eslint/js';
import { defineConfig } from 'eslint/config';
import tseslint from 'typescript-eslint';
export default defineConfig(
js.configs.recommended,
tseslint.configs.recommended,
);
Essa é uma base funcional: as regras recomendadas do core do ESLint mais o conjunto recomendado do typescript-eslint, que já especifica o parser e o plugin do typescript-eslint para você. defineConfig() vem do core do ESLint e é o helper indicado hoje, porque o typescript-eslint depreciou seu próprio tseslint.config() em favor dele. O helper antigo ainda funciona, então uma configuração que já roda não está quebrada, mas novos setups devem usar defineConfig(). De qualquer forma, continue importando tseslint, já que você ainda precisa dele para tseslint.configs.* e para os helpers de glob.
Aumente o rigor e, depois, ajuste regras individuais
recommended é o ponto de partida; dois presets opcionais elevam o nível. tseslint.configs.strict adiciona regras de correção mais opinativas, e tseslint.configs.stylistic adiciona regras de consistência que não precisam de informação de tipos. Adicione-os ao lado de recommended no array de configuração.
Sobrescreva qualquer regra em um bloco rules. As severidades vêm em três níveis: off (ou 0) desliga a regra por completo, warn (ou 1) reporta o problema sem afetar o exit code, e error (ou 2) reporta e faz o ESLint sair com código 1. Use warn para coisas que você quer visíveis, mas não bloqueantes; use error para qualquer coisa que não pode chegar ao repositório, já que ele encerra com código diferente de zero e quebra o CI.
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': 'error',
}
Prefira severidades em string ('warn'/'error') à forma numérica em configurações modernas. Elas são mais legíveis, e o estilo puramente numérico é a marca registrada de tutoriais datados de .eslintrc.
Linting type-aware: as regras que precisam de informação de tipos
Algumas das regras mais valiosas, entre elas no-floating-promises e no-misused-promises, precisam de informação de tipos, e você a habilita adicionando parserOptions: { projectService: true }. Essa tem sido a forma recomendada de ativar o linting tipado desde o typescript-eslint v8, substituindo a antiga opção project porque exige menos configuração e roda mais rápido. Troque também seus presets pelas variantes type-checked (recommendedTypeChecked, strictTypeChecked, stylisticTypeChecked). Um parserOptions: {} vazio não ativa o linting type-aware, um erro comum em configurações copiadas.
{
files: ['**/*.ts', '**/*.tsx'],
extends: [tseslint.configs.recommendedTypeChecked],
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
}
O linting tipado tem um custo real. Ativá-lo significa que o TypeScript precisa compilar seu projeto antes que o ESLint possa fazer o lint, o que representa um ou dois segundos em uma base pequena e um tempo perceptivelmente maior em uma grande. A própria recomendação do typescript-eslint se apoia em uma assimetria aqui: plugins de editor mantêm cache das informações de tipo e escapam em grande parte dessa penalidade, então rode o lint tipado completo no CI e no pre-commit, e deixe o editor cobrir o dia a dia. O projectService também elimina a antiga gambiarra de manter um tsconfig.eslint.json separado, já que ele usa o mesmo projeto que o editor usa.
Separe JS de TS e defina seus ignores
Regras type-checked só fazem sentido em arquivos que o TypeScript entende, então limite-as a **/*.ts/**/*.tsx e desligue-as para JavaScript puro. O typescript-eslint oferece um preset exatamente para isso. Sua própria documentação aplica tseslint.configs.disableTypeChecked a um bloco **/*.js para remover a configuração específica de TypeScript. No flat config, os ignores são apenas um bloco de configuração contendo somente a chave ignores, e é isso que substitui o .eslintignore.
// eslint.config.mjs
import js from '@eslint/js';
import { defineConfig } from 'eslint/config';
import tseslint from 'typescript-eslint';
import prettier from 'eslint-config-prettier';
export default defineConfig(
{ ignores: ['dist/', 'node_modules/', 'coverage/', '**/*.d.ts'] },
js.configs.recommended,
{
files: ['**/*.ts', '**/*.tsx'],
extends: [tseslint.configs.recommendedTypeChecked],
languageOptions: {
parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname },
},
rules: { '@typescript-eslint/no-explicit-any': 'warn' },
},
{ files: ['**/*.js', '**/*.mjs'], extends: [tseslint.configs.disableTypeChecked] },
prettier, // deve vir por último
);
Deixe o Prettier formatar e, então, monte o fluxo de trabalho
Mantenha a formatação fora do ESLint. Adicione o eslint-config-prettier por último para desligar as regras estilísticas do ESLint que brigariam com o Prettier, e fixe-o em ^10.1.8 ou posterior. Essa versão importa: em julho de 2025, um ataque de phishing às credenciais npm de um mantenedor levou a quatro releases adulteradas, catalogadas como CVE-2025-54313. As versões 8.10.1, 9.1.1, 10.1.6 e 10.1.7 traziam um script de postinstall que executava um payload DLL empacotado em máquinas Windows, e as releases corrigidas são 8.10.2, 9.1.2 e 10.1.8. Apenas essas quatro foram afetadas e o payload só era executado no Windows, de modo que builds anteriores limpas, como a 10.1.5, nunca foram comprometidas. Rodar o Prettier como uma regra do ESLint via eslint-plugin-prettier é possível, mas opcional; muitos times dispensam isso porque torna o linting mais lento e ruidoso.
Adicione um script de lint. Nenhuma flag --ext é necessária, porque a seleção de arquivos vive no glob files de cada bloco de configuração:
{
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix"
}
}
A partir daí, execute eslint --fix nos arquivos em stage com Husky e lint-staged antes de cada commit, habilite o fix-on-save no VS Code via "source.fixAll.eslint": "explicit" em codeActionsOnSave, e rode eslint . como uma etapa do CI para que uma regra falhando bloqueie o merge.
Uma última coisa que vale a pena considerar: o ESLint 9 chegou ao fim da vida útil em 2026-08-06 e não recebe mais atualizações. Se você ainda está no ESLint 9, a configuração acima funciona sem alterações no ESLint 10, então atualize o runtime e siga em frente. Comece pela configuração mínima de duas linhas, adicione recommendedTypeChecked com projectService quando quiser as regras de segurança para promises, e coloque o eslint-config-prettier por último.
Perguntas Frequentes
Devo habilitar o linting type-aware, e qual é o custo?
Habilite se você quiser as regras de correção de maior valor, como no-floating-promises e no-misused-promises, que não funcionam sem informação de tipos. O custo é que o ESLint pede ao TypeScript que compile seu projeto antes do lint, o que é desprezível em projetos pequenos, mas perceptível em grandes. A maioria dos times roda o lint tipado completo no CI e no pre-commit e conta com o cache da IDE no editor, onde a penalidade é evitada.
Qual é a diferença entre projectService e project para linting tipado?
Ambos habilitam o linting tipado, mas o projectService é o que o typescript-eslint recomenda a partir da v8 por ser mais fácil de configurar e mais rápido, já que reutiliza o mesmo tsconfig.json que seu editor já usa. A opção mais antiga project exige que você aponte para um ou mais arquivos TSConfig por caminho e frequentemente obrigava os times a manter um tsconfig.eslint.json separado. Use projectService: true a menos que tenha um motivo específico para não fazê-lo.
A flag --ext ainda funciona no flat config do ESLint?
Não, --ext não é mais necessária no flat config. A seleção de arquivos vive dentro do glob files de cada bloco de configuração, por exemplo files: ['**/*.ts', '**/*.tsx'], então o ESLint já sabe quais arquivos analisar. Seu script de lint passa a ser apenas eslint . sem flag de extensão. Scripts que ainda passam --ext foram copiados de tutoriais anteriores ao flat config, escritos para o sistema eslintrc removido.
Devo usar eslint-config-prettier ou eslint-plugin-prettier?
Use eslint-config-prettier na maioria dos projetos. Ele desliga as regras estilísticas do ESLint que conflitam com o Prettier e não adiciona overhead de execução; coloque-o por último no seu array de configuração. A abordagem com eslint-plugin-prettier roda o Prettier como uma regra de lint de verdade, o que é opcional e mais lento, e expõe cada diferença de formatação como um erro de lint. Fixe o eslint-config-prettier em 10.1.8 ou posterior para se manter longe do incidente de supply chain de julho de 2025.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k