Para desenvolvedores front-end com experiência em TypeScript, o debate entre “Interface vs. Type sobre interface” é comum. No início, ambos pareciam equivalentes, mas em projetos maiores, pequenas decisões como essa impactam a consistência do código. Este artigo explora a criação de uma regra ESLint personalizada para impor o uso de type aliases em vez de declarações de interface, detalhando os motivos, a construção da regra e sua transformação no fluxo de trabalho.
O Caos de Escalar uma Equipe Sem Regras Claras
Inicialmente, a equipe de desenvolvimento era pequena e um acordo verbal favorecia o uso de type aliases em vez de interface declarations, devido à sua previsibilidade. Essa abordagem funcionou bem até o rápido crescimento da equipe, que atingiu dez desenvolvedores.
Novos membros nem sempre conheciam a regra não escrita, e alguns preferiam interface, familiar de suas experiências com Java. A mistura de abordagens gerou debates intermináveis nas revisões de código, focando mais em estilo do que em conteúdo. Para resolver isso, foi criado um ESLint plugin personalizado, o eslint-plugin-interface-to-type, para impor o uso de type em toda a base de código.
A regra automatizada economizou tempo e evitou discussões desnecessárias, garantindo que todos seguissem o mesmo padrão. A padronização do código é fundamental para manter a qualidade e facilitar a colaboração em projetos de grande escala. Além disso, a automação reduz a probabilidade de erros humanos e inconsistências.
Com a regra implementada, o time pôde se concentrar em aspectos mais importantes do desenvolvimento, como a arquitetura e a otimização de performance, em vez de se preocupar com questões de estilo. Essa mudança resultou em um fluxo de trabalho mais eficiente e produtivo. A consistência no código também facilitou a integração de novos membros à equipe, já que eles não precisavam aprender regras não documentadas.
Por Que Escolher type Sobre interface
À primeira vista, type e interface parecem intercambiáveis, definindo formatos para objetos. No entanto, existe uma diferença sutil que pode causar problemas em projetos maiores: o Declaration Merging. Em TypeScript, duas definições de interface com o mesmo nome são automaticamente combinadas.
Esse comportamento pode ser útil para estender tipos de bibliotecas de terceiros. No entanto, em um ambiente colaborativo, isso pode gerar confusão. Imagine dois desenvolvedores declarando propriedades conflitantes em arquivos separados sem saber. Isso pode levar a bugs difíceis de detectar. Por outro lado, type aliases não são mesclados.
Se você tentar declarar dois type aliases com o mesmo nome, o TypeScript exibirá um erro de compilação, forçando a resolução do conflito. Essa previsibilidade é crucial para evitar problemas. Além disso, type aliases são mais flexíveis, representando uniões, interseções e primitivos, algo que interface não faz tão bem.
Embora interface tenha seu lugar, como ao estender classes, type é mais seguro para a maioria dos casos de uso. A escolha entre type e interface deve ser baseada nas necessidades específicas do projeto e da equipe. Em projetos onde a consistência e a prevenção de conflitos são prioridades, type aliases podem ser a melhor opção.
Construindo a Regra ESLint: Do Caos à Consistência
Para impor o uso de type, foi criado o ESLint plugin eslint-plugin-interface-to-type. O objetivo era sinalizar qualquer declaração de interface, sugerir a substituição por um type alias e fornecer uma opção de correção automática para simplificar o processo.
Criar uma regra ESLint personalizada foi desafiador, mas após pesquisa, foi possível entender o processo. O plugin usa a AST (Abstract Syntax Tree) do ESLint para detectar declarações de interface e transformá-las em type aliases equivalentes. Por exemplo, uma declaração de interface como:
interface User {
id: number;
name: string;
role: 'admin' | 'user';
}
é sinalizada e pode ser automaticamente corrigida para:
type User = {
id: number;
name: string;
role: 'admin' | 'user';
};
A parte mais difícil foi lidar com casos extremos, como declarações de interface que estendem outras interfaces ou possuem generics complexos. Foi necessário ajustar a regra para garantir a compatibilidade e evitar falsos positivos. Implementar o recurso de correção automática também foi um desafio, pois o ESLint requer transformações precisas.
Para configurar o plugin, basta instalá-lo como uma dependência de desenvolvimento:
npm install eslint-plugin-interface-to-type --save-dev
E adicioná-lo à configuração do ESLint:
{
"plugins": ["interface-to-type"],
"rules": {
"interface-to-type/prefer-type-over-interface": "error"
}
}
Ao executar o ESLint com — fix, ele converterá automaticamente as declarações de interface em type aliases.
Impacto no Fluxo de Trabalho: Menos Discussões, Mais Construção
O impacto da regra na equipe foi imediato. As discussões sobre interface vs. type nas revisões de código diminuíram drasticamente. A regra impôs a consistência, liberando espaço mental para discussões mais importantes, como arquitetura e otimizações de desempenho.
A regra também economizou tempo, eliminando a necessidade de longas explicações sobre preferências de estilo para novos desenvolvedores. As revisões de código se tornaram mais rápidas, pois não havia mais detalhes sobre sintaxe. Essa pequena mudança teve grandes efeitos. A padronização do código também facilitou a manutenção e a evolução do projeto a longo prazo.
Além disso, a regra ajudou a evitar erros e inconsistências que poderiam surgir do uso inconsistente de interface e type. Ao garantir que todos seguissem o mesmo padrão, o time pôde se concentrar em entregar um produto de alta qualidade. A automação do processo também reduziu a carga de trabalho dos desenvolvedores, permitindo que eles se concentrassem em tarefas mais desafiadoras e criativas.
Dicas Para Desenvolvedores: Como Fazer Isso Funcionar Para Sua Equipe
Se você está interessado nessa ideia, aqui estão algumas dicas para ajudá-lo a impor o uso de type ou qualquer outro padrão de codificação em seus projetos:
1. Entenda Suas Necessidades Primeiro: Antes de impor type sobre interface, verifique se isso se encaixa nos objetivos da sua equipe. Se você depende muito de Declaration Merging ou implementações de classe, interface ainda pode ter um lugar. Para nós, type funcionou melhor em 99% das vezes, então fez sentido padronizar.
2. 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.
3. Incentive o Buy-In 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 o input deles me ajudou a detectar alguns casos extremos que eu havia perdido.
Adotar type aliases e aplicar essa prática com uma regra ESLint personalizada transformou a maneira como a equipe trabalha. O código ficou mais previsível, as revisões mais produtivas e a integração de novos membros, mais suave. Se você enfrenta desafios similares ou deseja experimentar padrões mais rigorosos, essa pode ser uma boa solução.
Comece instalando o eslint-plugin-interface-to-type e executando-o em sua base de código. Veja como é ter uma abordagem consistente para os tipos TypeScript. Tem uma perspectiva diferente sobre interface vs. type? Ou a regra revelou problemas inesperados em seu projeto? Deixe um comentário abaixo.
Primeira: Este conteúdo foi auxiliado por Inteligência Artificiado, mas escrito e revisado por um humano.
Segunda: Via Dev.to