Ícone do site Café Codificado

TypeScript: `type` vs `interface` para iniciantes e juniores, quando usar cada um e evitar bugs

TypeScript: `type` vs `interface` para iniciantes e juniores, quando usar cada um e evitar bugs

No universo do desenvolvimento com TypeScript, a escolha entre `type` (type alias) e `interface` pode gerar dúvidas, especialmente para quem está começando ou atua em níveis júnior. Embora ambos sirvam para definir a estrutura de dados, entender suas nuances é crucial para escrever código mais limpo, previsível e com menos chances de erros silenciosos.

A discussão sobre qual deles é superior ou quando utilizá-los é um tema recorrente. Enquanto alguns defendem o uso exclusivo de `type` por sua previsibilidade, outros enxergam valor no merge automático das `interfaces`, especialmente em cenários específicos como a criação de bibliotecas.

Este artigo explora os argumentos a favor do uso predominante de `type` para a maioria dos cenários de desenvolvimento, destacando como ele pode otimizar o fluxo de trabalho e a manutenção do código. Conforme compartilhado por desenvolvedores experientes, o objetivo é disseminar boas práticas que facilitem o aprendizado e a produtividade de toda a comunidade.

A busca pela fonte única de verdade em modelagem de dados

Para garantir a consistência em projetos de software, é fundamental estabelecer uma **fonte única de verdade** para a modelagem dos tipos de dados. Essa prática, semelhante à criação de esquemas em bancos de dados, aplica-se a tudo, desde modelos de API e definições de objetos até entidades de negócio, facilitando enormemente a implementação de novas funcionalidades.

A argumentação principal para priorizar `type` reside na sua natureza determinística. Ao contrário das `interfaces`, que possuem um recurso de **merge automático** — permitindo que propriedades sejam adicionadas a um contrato existente de forma implícita —, os `type aliases` não oferecem essa funcionalidade de maneira automática. Isso significa que qualquer extensão de um tipo precisa ser explícita, tornando o código mais previsível e menos propenso a inconsistências que podem passar despercebidas.

O poder dos Union Types e Intersection Types com `type`

Os `type aliases` em TypeScript se destacam quando combinados com recursos como **union types** (operador `|`, ou) e **intersection types** (operador `&`, e). Com `union types`, é possível definir um tipo que pode ser uma de várias opções literais. Por exemplo, um `type Contrato = { tipo: ‘Político’ | ‘Social’ }` garante que a propriedade `tipo` só poderá aceitar os valores ‘Político’ ou ‘Social’.

Essa abordagem é extremamente útil para habilitar o **autocompletar** no editor de código, melhorar a documentação e simplificar a definição de responsabilidades, além de promover a reutilização de lógica. Ao combinar um `type` com outro através de **intersection types**, como em `type Emenda = { tipoEntidade: ‘Responsável’ | ‘Agente Fiscal’ } & Contrato`, você cria um novo tipo que herda todas as propriedades de ambos os tipos originais.

Essa combinação não só reforça a estrutura de dados, mas também abre portas para o uso de utilitários mágicos do TypeScript. É possível, por exemplo, criar variações de um tipo que tornem suas propriedades opcionais ou omitam elementos específicos, como um `id`, garantindo otimização, melhor onboarding de novos desenvolvedores e documentação automática.

Por que evitar o merge automático das `interfaces`?

O principal ponto de discórdia é o **merge automático** das `interfaces`. Se uma `interface` com o mesmo nome é declarada em outro lugar do código, suas propriedades se fundem. Embora isso possa parecer conveniente em alguns casos, como na extensão de contratos em bibliotecas externas onde não se pode controlar as extensões do usuário, para o desenvolvimento interno de um projeto, essa característica pode ser uma fonte de **bugs silenciosos**.

Essa fusão implícita, se não for intencional e bem compreendida, pode levar a comportamentos inesperados e a um tempo considerável gasto em depuração. Em contrapartida, os `type aliases` oferecem um controle mais explícito sobre a extensão e composição de tipos, alinhando-se com a ideia de que o código deve ser o mais determinístico e previsível possível, similar à preferência por `const` em vez de `let` quando o valor não precisa ser reatribuído.

Cenários onde `interface` ainda pode ser considerada

Apesar da forte argumentação a favor dos `type aliases`, é importante reconhecer que as `interfaces` ainda possuem seus méritos. Um cenário comum onde o merge automático de `interface` pode ser vantajoso é na definição de contratos de configuração ou variáveis de ambiente. Por exemplo, ao definir uma `interface Env` com propriedades como `DB_PATH` e `JWT`, o merge automático pode ser útil para garantir que essas variáveis existam e estejam tipadas corretamente, prevenindo verificações desnecessárias contra `undefined`.

Além disso, as `interfaces` são frequentemente consideradas idiomáticas para firmar um contrato explícito entre diferentes partes de um objeto ou entre objetos. O TypeScript, em sua essência, oferece uma base de tipagem bastante flexível, e a discussão entre `type` e `interface` muitas vezes envolve preferências idiomáticas e exceções baseadas em casos de uso específicos.

Sair da versão mobile