12k
All articles

Melhorando a Qualidade do Código JavaScript com eslint-plugin-unicorn

eslint-plugin-unicorn para ESLint 10: instalação com flat config, presets recommended ou unopinionated e regras autofixáveis para melhorar a qualidade de JS e TS.

OpenReplay Team
OpenReplay Team
Melhorando a Qualidade do Código JavaScript com eslint-plugin-unicorn

eslint-plugin-unicorn é um plugin ESLint extenso e opinativo — com mais de 300 regras — que promove idiomas modernos de JavaScript e TypeScript, detecta bugs sutis de qualidade, e a maioria das suas regras é corrigível automaticamente. Ele requer ESLint >=10.4, flat config e ESM, o que significa que a configuração antiga com .eslintrc e extends: 'plugin:unicorn/recommended' não se aplica mais. Se você já utiliza ESLint com uma configuração básica e deseja elevar a qualidade do código sem criar regras personalizadas, este plugin é a forma mais rápida de fazê-lo.

O ponto de atenção é que “opinativo” tem dois lados. Algumas regras previnem loops acidentalmente quadráticos e bugs reais de corretude; outras impõem preferências estilísticas que podem conflitar com uma base de código já estabelecida. Este guia separa os dois casos: o que o plugin impõe, quais regras justificam seu uso, quais rebaixar ou desativar, e um eslint.config.mjs pronto para copiar e colar que funciona com as ferramentas atuais.

Principais Conclusões

  • eslint-plugin-unicorn inclui mais de 300 regras, a maioria corrigível automaticamente e, a partir da v69 (junho de 2026), requer ESLint ≥10.4, flat config e ESM — o .eslintrc não funciona mais porque o ESLint 10 o removeu completamente.
  • O caminho de adoção mais simples: estenda unicorn/recommended, execute eslint --fix e, em seguida, desative as poucas regras que conflitam com sua base de código — ou comece com o preset unopinionated, que já desativa as regras opinativas por padrão.
  • O plugin exporta três presets: recommended, unopinionated (recommended sem as regras opinativas) e all.
  • prefer-set-has transforma buscas acidentalmente quadráticas com .includes() em loops em verificações O(n) com Set.has(), e no-array-for-each reescreve .forEach() para for…of, proporcionando melhor narrowing de tipos no TypeScript.
  • Combine-o com @typescript-eslint/recommended-type-checked para unir a imposição de idiomas com linting orientado a tipos.

O que é eslint-plugin-unicorn e como ele funciona

eslint-plugin-unicorn é uma coleção de regras ESLint que orienta o código para padrões modernos e menos propensos a erros em JavaScript e TypeScript — preferindo verificações de pertencimento com Set em vez de varreduras em arrays, for…of em vez de .forEach(), especificadores de importação node: em vez de nomes de módulos simples, entre outros. Ele requer ESLint >=10.4, flat config e ESM. O repositório descreve-se como tendo mais de 300 regras, e a versão mais recente é a 69.0.0, com lançamentos aproximadamente mensais.

Dois aspectos de design moldam a forma como você o adota. Primeiro, a maioria das regras é corrigível automaticamente, portanto uma grande parte das violações é resolvida com eslint --fix em vez de edição manual. Segundo, o plugin inclui configs de preset para que você não precise habilitar as regras uma a uma. O plugin exporta uma config recomendada que impõe boas práticas e, separadamente, uma config all que habilita todas as regras, exceto as depreciadas. A tabela de regras usa emojis para indicar o status: ✅ para regras definidas na configuração recomendada, ☑️ para a configuração unopinionated e 🔧 para regras corrigíveis automaticamente pela opção de CLI --fix.

O escopo abrange principalmente JavaScript e TypeScript, com uma observação útil: embora a maioria das regras seja voltada para JavaScript e TypeScript, algumas também fazem linting de outros tipos de arquivo quando usadas com o plugin de linguagem ESLint correspondente, como @eslint/css, @eslint/json, @eslint/markdown ou @html-eslint/eslint-plugin. Para a maioria das equipes, as regras de JS/TS são o principal motivo para instalá-lo.

Instalando eslint-plugin-unicorn com flat config e ESM

Instale o plugin e um ESLint compatível, depois estenda o preset recomendado dentro de um eslint.config.mjs no formato flat. O requisito atual é ESLint 10.x — o ESLint 10 removeu completamente o sistema legado eslintrc, tornando o flat config o único formato suportado; portanto, qualquer tutorial que mostre .eslintrc.json com extends: 'plugin:unicorn/recommended' está desatualizado. O ESLint requer um runtime Node.js atual (Node 20.19+, 22.13+ ou 24+, conforme a página do ESLint no npm).

npm install --save-dev eslint eslint-plugin-unicorn

Uma configuração mínima funcional com escopo para arquivos JavaScript e TypeScript:

// eslint.config.mjs
import unicorn from 'eslint-plugin-unicorn';
import { defineConfig } from 'eslint/config';

export default defineConfig([
  {
    files: ['**/*.{js,mjs,cjs,ts,tsx}'],
    extends: [unicorn.configs.recommended],
    rules: {
      // substituições vão aqui
    },
  },
]);

Isso espelha o uso oficial: importe unicorn de 'eslint-plugin-unicorn', importe {defineConfig} de 'eslint/config' e, dentro de defineConfig, defina files e extends: [unicorn.configs.recommended]. Delimitar o escopo com files é importante quando você faz linting de linguagens não-JavaScript na mesma configuração — mantenha as regras JS do Unicorn em seu próprio objeto de configuração.

Há três presets para escolher seu ponto de partida:

  • recommended — o padrão curado.
  • unopinionated — recommended sem as regras opinativas.
  • all — todas as regras não depreciadas.

Se sua equipe achar as regras opinativas muito ruidosas, substitua unicorn.configs.recommended por unicorn.configs.unopinionated e evite boa parte da configuração manual descrita abaixo.

Uma nota sobre migração: se você ainda depende de um plugin que fornece apenas uma config no estilo eslintrc, envolva-o com FlatCompat do @eslint/eslintrc. Use o @eslint/eslintrc mais recente com ESLint 10.x.

As regras eslint-plugin-unicorn de maior valor, com exemplos antes e depois

As regras Unicorn de maior valor merecem ser adotadas primeiro porque cada uma previne um problema de desempenho, corretude ou legibilidade, em vez de impor uma preferência estética. Todos os exemplos abaixo são o resultado de eslint --fix, não código ideal escrito manualmente.

prefer-set-has — elimina buscas acidentalmente quadráticas

prefer-set-has sinaliza um array que é usado apenas para verificações de pertencimento com .includes() e orienta você a usar um Set. Ele prefere Set#has() em vez de Array#includes() ao verificar existência ou não existência. Isso é mais relevante dentro de loops: Array.prototype.includes é O(n) por chamada, portanto chamá-lo uma vez por iteração sobre outra coleção resulta em O(n²), enquanto Set.prototype.has é aproximadamente de tempo constante.

// ❌ O(n²): includes() varre o array inteiro a cada iteração
const allowed = ['read', 'write', 'admin', /* …centenas… */];
const granted = requested.filter((scope) => allowed.includes(scope));

// ✅ O(n): Set.has() é aproximadamente de tempo constante por busca
const allowed = new Set(['read', 'write', 'admin', /* …centenas… */]);
const granted = requested.filter((scope) => allowed.has(scope));

O modo de falha é silencioso: funciona bem com entradas pequenas e degrada acentuadamente conforme os dados crescem. Essas são exatamente as regressões de interação lenta que você acaba diagnosticando em produção, e não durante a revisão de código.

no-array-for-each — melhor narrowing de tipos no TypeScript e controle de fluxo

no-array-for-each reescreve callbacks de .forEach() para loops for…of. Ele prefere for…of ao método forEach. O ganho vai além do estilo: for…of oferece melhor narrowing de tipos TypeScript ao longo do corpo do loop, permite usar await diretamente sem as complicações de callbacks e suporta break/continue — nenhum dos quais funciona adequadamente dentro de um callback .forEach().

// ❌
users.forEach((user) => {
  sendEmail(user);
});

// ✅ corrigido automaticamente
for (const user of users) {
  sendEmail(user);
}

prefer-node-protocol — importações de módulos nativos do Node sem ambiguidade

prefer-node-protocol reescreve importações de módulos nativos sem prefixo para a forma explícita com node:. Ele prefere o uso do protocolo node: ao importar módulos nativos do Node.js. O prefixo explícito torna a importação inequivocamente um módulo nativo do Node e dificulta que seja sobrescrita por um pacote npm com o mesmo nome.

// ❌
import fs from 'fs';
import { join } from 'path';

// ✅ corrigido automaticamente
import fs from 'node:fs';
import { join } from 'node:path';

Mais algumas correções automáticas de alto valor

Cada uma delas mapeia um padrão legado para seu equivalente moderno e é corrigida automaticamente:

// throw-new-error — Exige new ao lançar um erro
throw Error('boom');        // ❌
throw new Error('boom');    // ✅

// prefer-string-replace-all — substituição global sem regex /g
str.replace(/-/g, ' ');     // ❌
str.replaceAll('-', ' ');   // ✅

// prefer-array-flat-map — colapsa map().flat()
rows.map((r) => r.cells).flat();   // ❌
rows.flatMap((r) => r.cells);      // ✅

// new-for-builtins — exige new para Map/Set/Array/Date, etc.
const cache = Map();        // ❌
const cache = new Map();    // ✅

A regra throw-new-error exige new ao criar um erro (cobrindo tanto const e = Error() quanto throw Error()), e ela está na config recomendada e é corrigível automaticamente. new-for-builtins impõe o uso de new para todos os builtins, exceto String, Number, Boolean, Symbol e BigInt. Array.prototype.flatMap é ao mesmo tempo mais claro e evita o array intermediário que .map().flat() aloca.

Duas regras voltadas para legibilidade completam o conjunto de alto valor. better-regex melhora expressões regulares tornando-as mais curtas e consistentes (por exemplo, [0-9] se torna \d). prefer-ternary colapsa blocos simples de if/else: ela prefere expressões ternárias a instruções if simples que retornam ou atribuem valores. Uma instrução if-else tipicamente produz mais linhas do que um ternário de linha única, e pode forçar você a declarar variáveis com let apenas para reatribuí-las nos blocos — deixando-as desnecessariamente mutáveis e impedindo que prefer-const sinalize a variável.

// ❌
function status(ok) {
  if (ok) {
    return 'pass';
  } else {
    return 'fail';
  }
}

// ✅ corrigido automaticamente
function status(ok) {
  return ok ? 'pass' : 'fail';
}

As regras controversas e como decidir

Algumas regras recomendadas impõem preferências em vez de corretude, e são as que geram mais ruído em uma base de código existente. A tabela abaixo apresenta a decisão de manter/avisar/desativar para os casos mais comuns, todos habilitados em recommended.

RegraVeredictoMotivo
prevent-abbreviationsAvisar ou delimitar escopoRenomeia eerror, reqrequest, propsproperties; conflita com nomes que seu framework e equipe já utilizam.
no-nullDesativar se você usa nullO Unicorn prefere undefined, mas null é legítimo quando um ORM ou API o retorna — não há uma resposta única correta.
no-array-reduceManter ou avisarDesabilita Array#reduce() e Array#reduceRight(); razoável para legibilidade, mas reduce é adequado para muitas equipes.
filename-caseDelimitar ignoresConflita com nomes de arquivos obrigatórios do framework; mantenha-a, mas exclua as exceções.

O caso de no-null é o mais claro: o Unicorn prefere undefined, mas null é o que muitos drivers de banco de dados e APIs HTTP retornam, então forçar um em vez do outro cria ruído de conversão sem nenhum benefício de corretude. Desative-a quando null faz parte do seu contrato de dados.

Você não precisa desativar essas regras manualmente. O preset unopinionated deixa as regras opinativas — incluindo no-null e prevent-abbreviations — desativadas por padrão, sendo o meio-termo nativo entre recommended e uma config altamente personalizada.

Para as regras que você mantém, mas deseja ajustar, delimite o escopo em vez de desativar. Um padrão comum para filename-case exclui arquivos de framework:

{
  files: ['**/*.{js,ts,tsx}'],
  extends: [unicorn.configs.recommended],
  rules: {
    'unicorn/no-null': 'off',
    'unicorn/prevent-abbreviations': 'warn',
    'unicorn/filename-case': [
      'error',
      {
        cases: { pascalCase: true, camelCase: true },
        ignore: ['next-env\\.d\\.ts', 'vite\\.config\\.ts'],
      },
    ],
  },
}

Adotando-o incrementalmente em uma base de código existente

Implante o plugin em etapas para que a primeira execução não o sobrecarregue com erros. A sequência que funciona:

  1. Comece com um preset. Estenda unicorn/recommended ou unicorn/unopinionated se as regras opinativas forem muito ruidosas para sua equipe.
  2. Aplique a correção automática primeiro. Execute npx eslint . --fix. Como a maioria das regras é corrigível automaticamente, uma grande fração das violações desaparece aqui, e você revisa um único diff mecânico em vez de centenas de erros inline.
  3. Rebaixe as regras mais ruidosas para warn. Qualquer coisa que ainda dispare em grande volume e não seja um problema de corretude — prevent-abbreviations é o culpado habitual — vai para 'warn' para não bloquear o CI durante a migração.
  4. Delimite ignores em vez de desativar completamente. Use a opção ignore (como com filename-case) e globs de files para criar exceções em vez de eliminar uma regra útil em todo o projeto.
  5. Adicione linting orientado a tipos. Inclua @typescript-eslint/recommended-type-checked junto com o Unicorn. O Unicorn impõe idiomas; a config type-checked do typescript-eslint detecta bugs que apenas o sistema de tipos consegue ver. Os dois são complementares, e habilitar ambos é um padrão sólido para projetos TypeScript.

Trate sua configuração como um documento vivo. À medida que você atualiza o plugin — os lançamentos chegam aproximadamente mensalmente — verifique novamente a tabela de regras em busca de novas regras e renomeações, e reavalie quais regras em warn estão prontas para ser promovidas a error.

O caminho mais rápido para um JavaScript melhor com eslint-plugin-unicorn não tem glamour: estenda unicorn/recommended, execute eslint --fix e dedique sua atenção apenas às poucas regras opinativas que genuinamente conflitam com a forma como sua equipe escreve código. Comece com o preset hoje, deixe o autofixer fazer a maior parte do trabalho e ajuste a partir daí — a tabela oficial de regras é a referência para o status atual e as opções de cada regra.

Perguntas Frequentes

eslint-plugin-unicorn funciona junto com typescript-eslint, ou eles entram em conflito?

Eles são complementares e projetados para funcionar juntos. O Unicorn impõe idiomas modernos de JavaScript e TypeScript, enquanto o typescript-eslint adiciona regras orientadas a tipos que detectam bugs que apenas o sistema de tipos consegue ver. Adicione ambos como objetos de configuração separados no seu flat eslint.config.mjs e combine unicorn/recommended com a config recommended-type-checked do typescript-eslint. Habilitar ambos é um padrão sólido para projetos TypeScript, sem sobreposição de regras a resolver.

Qual é a diferença entre os presets recommended e unopinionated?

O preset recommended é o padrão curado que habilita tanto regras de corretude quanto regras estilísticas opinativas. O preset unopinionated é o recommended sem as regras opinativas, portanto desativa previamente entradas controversas como no-null e prevent-abbreviations que conflitam com bases de código estabelecidas. Escolha unopinionated quando sua equipe achar as regras estilísticas muito ruidosas; ele é o meio-termo nativo entre recommended e uma config altamente personalizada. Um terceiro preset, all, habilita todas as regras não depreciadas.

Por que minha config antiga com .eslintrc e extends plugin:unicorn/recommended parou de funcionar?

O ESLint 10 removeu completamente o sistema legado eslintrc, deixando o flat config como o único formato suportado, e o eslint-plugin-unicorn requer ESLint 10.4 ou superior, flat config e ESM. A sintaxe antiga com .eslintrc e a string extends 'plugin:unicorn/recommended' não funciona mais. Migre para um eslint.config.mjs que importe unicorn de 'eslint-plugin-unicorn' e defina extends como unicorn.configs.recommended dentro de um objeto de configuração delimitado com files.

Adicionar eslint-plugin-unicorn vai quebrar meu build com centenas de erros?

A maioria das violações é resolvida automaticamente porque a maior parte das regras do unicorn é corrigível automaticamente. Execute o eslint com a flag fix primeiro, o que elimina uma grande fração dos problemas como um único diff mecânico em vez de centenas de erros inline. Para regras que ainda disparam em grande volume, mas não são problemas de corretude — como prevent-abbreviations — defina-as como warn para que não bloqueiem o CI durante a migração. Essa implantação em etapas mantém o build estável enquanto você adota o plugin.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.