Com mais de seis anos de experiência como desenvolvedor front-end, debates sobre TypeScript são comuns. Um tema que sempre gera discussões é o dilema clássico “Type sobre interface“. No início da minha carreira, não pensava muito nisso — ambos pareciam resolver o problema. Mas, à medida que os projetos cresciam e as equipes se expandiam, percebi que pequenas decisões como essa podem construir ou destruir a consistência do código. Por isso, criei uma regra ESLint personalizada para forçar o uso de type em vez de declarações de interface em nossa base de código TypeScript.
Neste artigo, vou compartilhar o porquê dessa escolha, como construí a regra e como ela transformou nosso fluxo de trabalho. Além disso, darei algumas dicas para você experimentar também.
O Caos de Dimensionar uma Equipe Sem Regras Claras
Quando comecei a trabalhar com TypeScript na minha empresa, nossa equipe era pequena — apenas alguns desenvolvedores. Tínhamos um acordo verbal para usar type em vez de declarações de interface, pois achávamos mais previsível. No começo, funcionou muito bem. Estávamos alinhados, nosso código era limpo e tudo ia bem. Mas, então, nossa equipe cresceu rápido.
Em um ano, tínhamos 10 desenvolvedores contribuindo para a mesma base de código, e foi aí que as coisas ficaram complicadas. Novos membros da equipe nem sempre estavam cientes da nossa “regra não escrita”. Alguns preferiam interface porque parecia mais familiar devido à experiência com Java. Outros misturavam as duas abordagens sem perceber.
As revisões de código se transformaram em debates intermináveis sobre estilo, em vez de substância. Ficou claro que precisávamos de uma maneira melhor de garantir a consistência — algo automatizado, confiável e escalável. Então, decidi arregaçar as mangas e criar um plugin ESLint personalizado: eslint-plugin-interface-to-type. Ele aplicaria automaticamente o uso de type em vez de interface em toda a nossa base de código, nos salvando de nós mesmos.
Por Que Escolhi Type Sobre Interface
Antes de mergulhar nos detalhes técnicos, vamos falar sobre por que escolhi type em vez de interface. À primeira vista, podem parecer intercambiáveis — ambos definem formas para objetos, certo? Mas há uma diferença sutil que pode te prejudicar em projetos maiores: a mesclagem de declaração.
No TypeScript, se você declarar duas definições de interface com o mesmo nome, elas automaticamente se fundem em uma só. Aqui está um exemplo rápido:
interface User {
id: number;
}
interface User {
name: string;
}
// TypeScript merges them into:
interface User {
id: number;
name: string;
}
Esse comportamento pode ser útil em alguns casos, como estender tipos de bibliotecas de terceiros. Mas, em um ambiente colaborativo, é uma receita para a confusão. Imagine dois desenvolvedores declarando, sem saber, propriedades conflitantes em arquivos separados — é um bug esperando para acontecer.
Por outro lado, os type não se fundem. Se você tentar declarar dois type com o mesmo nome, o TypeScript lança um erro de compilação, forçando você a resolver o conflito de antemão. Para mim, essa previsibilidade é uma salvação.
Além da mesclagem de declaração, os type são mais flexíveis. Eles podem representar uniões, interseções e primitivos — coisas com as quais a interface tem dificuldades. Embora a interface tenha seu lugar (como quando você precisa estender classes), achei o type como o padrão mais seguro para a maioria dos nossos casos de uso.
Construindo a Regra ESLint: Do Caos à Consistência
Agora que eu tinha um motivo claro para impor o type, precisava de uma ferramenta para fazer isso acontecer automaticamente. É aí que entra meu plugin ESLint, eslint-plugin-interface-to-type. Eu queria uma regra que sinalizasse qualquer declaração de interface, sugerisse substituí-la por um type e até fornecesse uma opção de correção automática para agilizar o processo.
Construir uma regra ESLint personalizada foi um pouco assustador no início — eu nunca tinha escrito uma antes. Mas, depois de alguma pesquisa, peguei o jeito. O plugin usa a AST (Abstract Syntax Tree) do ESLint para detectar declarações de interface e transformá-las em type equivalentes. Por exemplo, isto:
interface User {
id: number;
name: string;
role: 'admin' | 'user';
}
é sinalizado e pode ser corrigido automaticamente para:
type User = {
id: number;
name: string;
role: 'admin' | 'user';
};
A parte mais difícil foi lidar com os casos extremos — como declarações de interface que estendem outras interfaces ou têm generics complexos. Passei horas ajustando a regra para garantir a compatibilidade e evitar falsos positivos. Outro desafio foi implementar o recurso de correção automática. A opção — fix
do ESLint exige que você forneça transformações precisas, então tive que mapear cuidadosamente a sintaxe da interface para a sintaxe do type sem quebrar o código.
Depois que o plugin estava pronto, configurá-lo foi simples. Basta instalá-lo como uma dependência de desenvolvimento:
npm install eslint-plugin-interface-to-type --save-dev
Em seguida, adicione-o à sua configuração ESLint:
{
"plugins": ["interface-to-type"],
"rules": {
"interface-to-type/prefer-type-over-interface": "error"
}
}
Execute o ESLint com — fix
e ele converterá automaticamente suas declarações de interface em type. Pronto!
Impacto no Nosso Fluxo de Trabalho: Menos Discussões, Mais Construção
O impacto dessa regra em nossa equipe foi imediato. Antes, passávamos boa parte das revisões de código discutindo sobre interface vs. type. Agora, a regra garante a consistência para nós, liberando espaço mental para discussões mais importantes — como arquitetura ou otimizações de desempenho.
Também economizamos tempo. Novos desenvolvedores não precisavam mais de uma longa palestra de integração sobre nossas preferências de estilo. A regra simplesmente cuidava disso. As revisões de código se tornaram mais rápidas porque não estávamos mais discutindo sobre sintaxe. É uma pequena mudança, mas os efeitos foram enormes.
Dicas para Desenvolvedores: Como Fazer Isso Funcionar para Sua Equipe
Se você está intrigado com essa ideia, aqui estão algumas dicas para ajudá-lo a impor o uso de type — ou qualquer padrão de codificação — em seus projetos:
- Entenda Suas Necessidades Primeiro: Antes de impor type sobre interface, certifique-se de que isso se encaixa nos objetivos da sua equipe. Se você depende muito da mesclagem de declaração ou de implementações de classe, a interface ainda pode ter um lugar. Para nós, o type funcionou melhor em 99% das vezes, então fez sentido padronizar nisso.
- Automatize com Ferramentas Como ESLint: Não confie em acordos verbais — eles não são escaláveis. Ferramentas como ESLint (ou Prettier para formatação) podem impor regras de forma consistente em toda a equipe. Escrever uma regra personalizada pode parecer intimidador, mas é uma ótima experiência de aprendizado e compensa a longo prazo.
- Incentive a Adesão da Sua Equipe: Se você estiver introduzindo um novo padrão, obtenha feedback de seus colegas primeiro. Compartilhei minha regra ESLint com a equipe antes de implementá-la, e a opinião deles me ajudou a identificar alguns casos extremos que eu havia perdido.
Experimente e Me Diga!
Mudar para type e impor isso com uma regra ESLint personalizada foi uma virada de jogo para minha equipe. Tornou nosso código mais previsível, nossas revisões mais produtivas e nossa integração mais tranquila. Se você está lidando com problemas semelhantes — ou apenas quer experimentar padrões mais rígidos — eu o encorajo a tentar.
Comece instalando eslint-plugin-interface-to-type e executando-o em sua base de código. Veja como é ter uma abordagem consistente para os tipos de TypeScript. Tem uma perspectiva diferente sobre interface vs. type? Ou a regra revelou alguns problemas inesperados em seu projeto? Deixe um comentário abaixo — adoraria ouvir sua opinião!
Primeira: Este conteúdo foi auxiliado por Inteligência Artificiado, mas escrito e revisado por um humano.
Segunda: Via Dev.to