Interface TypeScript vs tipo: diferenças reais, testadas com o compilador

  1. Início
  2. Blog
  3. Desenvolvimento de software

Desenvolvimento de Software

Interface TypeScript vs tipo: Diferenças reais, testadas com o compilador

As interfaces e os aliases de tipo se sobrepõem tanto que a escolha muitas vezes parece arbitrária. Aqui está o que cada um pode fazer e o outro não, confirmado por erros reais do compilador, além de uma regra simples que sua equipe pode seguir.

8 min de leitura
Interface TypeScript vs tipo: diferenças reais, testadas com o compilador

Principais pontos

  • Para formas de objetos simples, uma interface e um alias de tipo são quase intercambiáveis.
  • Somente type pode nomear uniões, tuplas e outros tipos que não sejam de objetos.
  • Somente interface suporta mesclagem de declarações, onde duas declarações são combinadas.
  • Uma regra comum: interfaces para formas de objetos, type para todo o resto, aplicadas de forma consistente.

Poucas questões sobre TypeScript causam tanto debate quanto Interface TypeScript versus tipo. Muitos artigos listam diferenças que costumavam ser importantes em versões mais antigas, mas agora estão desatualizadas. Então, em vez de repetir afirmações, nós as testamos.

Escrevemos um arquivo exercitando cada diferença e o verificamos com TypeScript 5.6.3 em modo estrito. Os erros do compilador citados abaixo vêm dessa execução. O comportamento pode mudar entre as versões, portanto, verifique a versão TypeScript que seu projeto usa. Se você é novo na linguagem, comece com nosso tutorial TypeScript para iniciantes.

A resposta curta

Um interface descreve a forma de um objeto e pode ser estendido ou reaberto. Um type alias dá um nome a qualquer tipo, incluindo objetos, uniões, tuplas e tipos de função. Onde ambos podem funcionar, a diferença é principalmente o estilo.

Onde eles são iguais

Estas duas declarações descrevem formas idênticas:

interface User {
  id: number;
  name: string;
  email?: string;
}

type UserAlias = {
  id: number;
  name: string;
  email?: string;
};

const a: User = { id: 1, name: "Asha" };
const b: UserAlias = a;   // no error

O TypeScript usa tipagem estrutural, o que significa que compara formas em vez de nomes. É por isso que um User pode ser atribuído a um UserAlias sem reclamação. Ambos suportam propriedades opcionais, readonly propriedades, métodos e assinaturas de índice.

Interface vs tipo lado a lado

Recurso interface tipo
Formas de objetos Sim Sim
Sindicatos, como "a" | "b" Não Sim
Tuplas e primitivas Não Sim
Tipos mapeados e condicionais Não Sim
Mesclagem de declarações Sim Não, é um erro
Estendendo estende & interseção
Propriedades conflitantes Erro na declaração Pode silenciosamente se tornar never
Usado com implements Sim Sim, se for um tipo de objeto

O que só o tipo pode fazer

Muitos tipos úteis não são formas de objetos e apenas um alias de tipo pode nomeá-los:

type Status = "pending" | "paid" | "shipped";   // union
type Point = [number, number];                    // tuple
type Handler = (event: string) => void;           // function type
type Keys = keyof User;                           // computed type
type ReadonlyUser = Readonly<User>;               // utility type

Os tipos de união estão entre os recursos mais valiosos do TypeScript, porque o compilador rejeita qualquer valor fora da lista:

let s: Status = "paid";
s = "refunded";

// error TS2322: Type '"refunded"' is not assignable to type 'Status'.

Isso detecta erros de digitação e valores desatualizados em tempo de compilação, antes que cheguem à produção.

Mesclagem de declarações

Se você declarar uma interface com o mesmo nome duas vezes, o TypeScript as mesclará em uma:

interface Settings {
  theme: string;
}
interface Settings {
  language: string;
}

const settings: Settings = { theme: "dark", language: "en" };   // no error

Tente a mesma coisa com um alias de tipo e o compilador recusa:

type Config = { theme: string };
type Config = { language: string };

// error TS2300: Duplicate identifier 'Config'.

Mesclar é como as bibliotecas permitem estender seus tipos. Por exemplo, você pode adicionar uma propriedade personalizada à interface global Window ou ao objeto de solicitação de uma estrutura, sem modificar a biblioteca. Isso é genuinamente útil.

O outro lado é que a fusão pode acontecer por acidente. Se dois arquivos em um projeto declaram uma interface com o mesmo nome no mesmo escopo, eles se combinam silenciosamente, o que pode ser confuso. Um alias de tipo torna essa colisão um erro.

estende vs interseções

Ambos permitem desenvolver um tipo existente:

interface Admin extends User {
  permissions: string[];
}

type AdminAlias = UserAlias & { permissions: string[] };

Para propriedades compatíveis, o resultado é o mesmo. A diferença aparece com um conflito. Quando uma interface tenta alterar o tipo de uma propriedade de forma incompatível, o compilador reporta isso imediatamente:

interface Base { id: number }
interface Broken extends Base { id: string }

// error TS2430: Interface 'Broken' incorrectly extends interface 'Base'.
//   Types of property 'id' are incompatible.
//     Type 'string' is not assignable to type 'number'.

Um cruzamento não reclama quando é declarado. Em vez disso, a propriedade conflitante se torna never, um tipo que nenhum valor pode ter. O erro só aparece mais tarde, quando você tenta utilizá-lo:

type Mixed = { id: number } & { id: string };
const m: Mixed = { id: 1 };

// error TS2322: Type 'number' is not assignable to type 'never'.

Esse erro posterior é mais confuso, pois aponta para longe do erro real. Esta é uma razão prática para preferir extends ao construir tipos de objetos.

Aulas e implementos

Uma classe pode prometer corresponder a uma forma usando implements, e isso funciona com uma interface ou um alias de tipo de objeto:

class Account implements User {
  constructor(public id: number, public name: string) {}
}

Isto compilado sem erros. Observe que implements apenas verifica se a classe corresponde. Ele não adiciona nenhuma propriedade ou código. Uma classe não pode implementar um tipo de união, pois não existe uma forma única para corresponder.

Somente leitura e tipos de utilitários

Tipos de utilitários como Readonly, Partial, Pick e Omit funcionam com interfaces e aliases. O resultado é um novo tipo, que normalmente é nomeado com um alias de tipo:

const r: ReadonlyUser = { id: 3, name: "Dev" };
r.name = "Chitra";

// error TS2540: Cannot assign to 'name' because it is a read-only property.

Lembre-se de que readonly é apenas uma verificação em tempo de compilação. Em tempo de execução, o objeto é JavaScript comum e ainda pode ser alterado por código que ignora os tipos.

Dica: Os tipos desaparecem quando o TypeScript é compilado para JavaScript. Os dados que chegam de fora, como uma resposta da API, não são verificados em tempo de execução apenas porque possuem um tipo. Valide-o, por exemplo, com uma biblioteca de esquemas. Nosso tutorial Fetch API aborda o tratamento seguro das respostas da API.

Desempenho do compilador

A orientação de desempenho da própria equipe do TypeScript sugere preferir interfaces que usam extends em vez de interseções grandes, porque o compilador pode armazenar em cache os relacionamentos de interface com mais eficiência. Em projetos pequenos e médios é improvável que você note alguma diferença. Pode ser importante em bases de código muito grandes com tipos profundamente compostos. Nós mesmos não medimos isso.

Qual você deve usar?

Uma regra amplamente utilizada, de acordo com as orientações do manual TypeScript, é:

  • Use interface para formas de objetos, especialmente APIs públicas e qualquer coisa que as classes implementem ou outras possam estender.
  • Use type para uniões, tuplas, tipos de função e tipos mapeados ou computados.
Você está definindo Usar
Um modelo de dados, como Usuário ou Pedido interface
Um conjunto de valores permitidos type união
Um par ou triplo de comprimento fixo type tupla
Uma assinatura de retorno de chamada tipo
Estendendo os tipos de uma biblioteca interface, através da fusão
Uma variação de um tipo existente type com um tipo de utilitário

Algumas equipes simplesmente usam type em todos os lugares, e isso também funciona bem. O que mais importa é a consistência, então escolha uma convenção, escreva-a em seu guia de estilo e deixe um linter aplicá-la. A regra consistent-type-definitions do ESLint do typescript-eslint pode fazer isso.

Erros comuns

  • Debate sem fim. Ambos são bons para objetos, então estabeleça a regra uma vez e siga em frente.
  • Mesclagem acidental. A reutilização de um nome de interface no mesmo escopo combina-os silenciosamente.
  • Interseções conflitantes. Eles criam never tipos com erros confusos posteriormente.
  • Tipos confiáveis ​​em tempo de execução. Eles são apagados, então valide os dados externos.
  • Usar any para silenciar erros. Desativa a verificação. Prefira unknown e restrinja-o.

Alternando entre eles em uma base de código existente

Alterar interface para type, ou o inverso, geralmente é seguro para formas de objetos simples, porque o TypeScript compara os tipos por sua estrutura, não por seus nomes. Algumas coisas ainda precisam ser verificadas antes de você mudar:

  • A conversão de uma interface em um alias de tipo remove a mesclagem de declarações, portanto, qualquer segunda declaração com o mesmo nome se torna um erro de identificador duplicado.
  • A conversão de um alias de tipo em uma interface funciona apenas para formas de objetos. Uniões, tuplas e tipos mapeados devem permanecer como aliases de tipo.
  • Mensagens de erro e atualizações do editor podem exibir o tipo de maneira diferente, o que pode surpreender os colegas de equipe, mas não altera o comportamento.

Faça a mudança em uma solicitação pull focada, execute o compilador no modo estrito e seu conjunto de testes e evite misturá-lo com o trabalho de recursos para que os revisores possam ver que nada mais mudou.

Considerações finais

A questão interface versus tipo tem uma resposta calma: eles se sobrepõem aos objetos, cada um tem algumas habilidades únicas e a consistência é mais importante do que a escolha. Use interfaces para formas e tipos para todo o resto, a menos que sua equipe prefira o contrário. Para ver as diferenças você mesmo, copie os exemplos acima em um arquivo e execute tsc --noEmit --strict nele.

Perguntas frequentes

Qual é a diferença entre interface e tipo no TypeScript?

Ambos podem descrever formas de objetos. Somente o tipo pode nomear uniões, tuplas e tipos computados, enquanto apenas a interface suporta a fusão de declarações.

Devo usar interface ou tipo para acessórios React?

Qualquer um funciona bem. Muitas equipes usam interfaces para adereços, enquanto outras preferem type para consistência com uniões. Escolha um e aplique-o em todo o projeto.

Um tipo pode estender uma interface?

Sim, usando uma interseção como type Admin = User & { permissions: string[] }. Uma interface também pode estender um alias de tipo de objeto.

A interface é mais rápida que o tipo?

A orientação de desempenho do TypeScript favorece a extensão da interface por grandes interseções em grandes bases de código. Em projetos típicos, é improvável que a diferença seja perceptível.

O que é mesclagem de declarações?

É quando duas interfaces com o mesmo nome no mesmo escopo se combinam em uma. Os aliases de tipo não podem ser mesclados e nomes duplicados causam um erro.

Continue aprendendo

Trabalhe com Stack Quora

Precisa de ajuda com testes, WordPress ou SEO?

Ajudamos as equipes a fornecer software melhor e a expandir seus sites. Conte-nos no que você está trabalhando e sugeriremos um próximo passo claro.

Continue lendo

Mais guias sobre testes, IA e desenvolvimento moderno

Gostou deste artigo? Escolha um tópico e vá direto para nossos tutoriais mais recentes.

Inteligência artificial

Explicação das alucinações de IA: por que os modelos de linguagem inventam as coisas e como reduzi-las

Explicação das alucinações de IA: o que são, por que os modelos de linguagem as produzem, tipos comuns, como reduzi-las com aterramento e instruções…

Leia o artigo →
DevOps

Variáveis de ambiente explicadas: arquivos .env, segredos e configuração bem feitos

Variáveis de ambiente explicadas com exemplos de Python que executamos: leitura e configuração, por que cada valor é uma string, arquivos .env, hera…

Leia o artigo →
Desenvolvimento de software

Explicação da limitação de taxa de API: algoritmos, cabeçalhos e tratamento de erros 429

Limitação de taxa de API explicada com uma simulação que executamos: token bucket, janela fixa, janela deslizante, 429 respostas, cabeçalhos de limi…

Leia o artigo →
Automação de testes

Zombando em Python: unittest.mock, patch e os erros que fazem os testes mentirem

Zombaria em Python com unittest.mock, testado: Mock e return_value, side_effect, patch onde um nome é usado, especificação e autospec, zombaria de d…

Leia o artigo →
Teste de software

Cobertura de código explicada: cobertura de linha versus filial e o que 100% realmente significa

Cobertura de código explicada com uma execução cobertura.py real: cobertura de linha, ramal e condição, leitura de um relatório, por que 100% de cob…

Leia o artigo →
Bancos de dados e SQL

DELETE vs TRUNCATE vs DROP em SQL: diferenças, reversão e quando usar cada um

DELETE vs TRUNCATE vs DROP explicado com SQL testado: o que cada um remove, rollback, aciona, redefinição de identidade, velocidade e como PostgreSQ…

Leia o artigo →
Bancos de dados e SQL

Exibições SQL explicadas: crie, use e evite as armadilhas, com exemplos

Visualizações SQL explicadas com consultas que executamos: criação de uma visualização, resultados ao vivo, ocultação de colunas, desempenho, visual…

Leia o artigo →
Desenvolvimento de software

Interface TypeScript vs tipo: diferenças reais, testadas com o compilador

Interface TypeScript versus tipo, verificada com o compilador real: o que ambos podem fazer, o que apenas um pode fazer, mesclagem de declarações, e…

Leia o artigo →
Desenvolvimento de software

Python *args e **kwargs explicados: funções flexíveis com exemplos claros

Python *args e **kwargs explicados com o código que executamos: empacotamento e descompactação, ordem dos parâmetros, argumentos somente de palavras…

Leia o artigo →
Desenvolvimento de software

Funções Python Lambda: sintaxe, usos reais e quando não usá-los

Funções lambda do Python explicadas com o código que executamos: sintaxe, classificação com chave, mapa e filtro, fechamentos, armadilha de ligação …

Leia o artigo →
Inteligência artificial

O que são agentes de IA? Como eles funcionam, com um loop de agente simples que você pode executar

O que são agentes de IA? Um guia simples para o loop do agente, ferramentas, memória e proteções, com um pequeno loop Python que executamos, riscos …

Leia o artigo →
DevOps

Monitoramento versus observabilidade: a verdadeira diferença, com logs, métricas e rastreamentos

Explicação do monitoramento versus observabilidade: falhas conhecidas e desconhecidas, logs, métricas e rastreamentos, um passo a passo do incidente…

Leia o artigo →
DevOps

Docker Volumes vs Bind Mounts: persista os dados do contêiner da maneira certa

Volumes Docker versus montagens de ligação explicadas: por que os dados do contêiner desaparecem, volumes nomeados, montagens de ligação, tmpfs, exe…

Leia o artigo →
Automação de testes

TDD vs BDD: principais diferenças, um recurso criado de ambas as maneiras e quando usar cada um

TDD vs BDD explicado com um recurso de desconto construído em ambos os sentidos: refatoração vermelho-verde com o código que executamos, cenários da…

Leia o artigo →
Desenvolvimento de software

Git merge vs rebase: diferenças, exemplos e quando usar cada um

Git merge vs rebase explicado com saída real do log, uma tabela comparativa, a regra de ouro do rebase e um fluxo de trabalho seguro para equipes.

Leia o artigo →
Desenvolvimento de software

List comprehension em Python: sintaxe, exemplos e quando evitar

List comprehension em Python explicada: sintaxe, filtros, if-else, laços aninhados, tempos medidos e quando um laço for comum é a melhor escolha.

Leia o artigo →
Bancos de dados e SQL

SQL JOIN explicado com exemplos (Inner, Left, Right, Full)

Entenda os joins SQL passo a passo com tabelas e consultas de exemplo: INNER, LEFT, RIGHT, FULL, CROSS e SELF, além de erros comuns e dicas de desem…

Leia o artigo →