Diferenças Entre Types e Interfaces no TypeScript: Guia Completo

Diferenças Entre Types e Interfaces no TypeScript: Guia Completo

“`html





Diferenças entre type e interface no TypeScript

Diferenças entre type e interfaces no TypeScript

No dia a dia parece que type e interface fazem “quase a mesma coisa”.
Mas existem diferenças importantes que afetam extensibilidade, composição e legibilidade do seu código.

Extensão: interface com extends
Composição: type via interseção (&)
Declaração merges: interface
Tipos avançados: type ganha

1) O que ambos fazem (e por que você usa os dois)

Tanto type quanto interface descrevem a forma de um valor no TypeScript.
Na prática, você está definindo um contrato: quais propriedades existem, que tipos elas possuem e quais restrições devem ser respeitadas.

Quando o seu objetivo é descrever objetos com propriedades, as duas abordagens costumam funcionar.
A diferença aparece quando você tenta ir além: criar uniões, fazer composição complexa, ou estender/mesclar definições em módulos e bibliotecas.

2) Extensão e composição: extends vs interseção (&)

Interfaces foram desenhadas com uma ênfase natural em herança de contratos.
Você estende uma interface com extends.

  • interface: “estende” outra definição
  • type: compõe tipos usando interseção (&) e pode usar uniões (|)

Em termos práticos: se você quer uma hierarquia clara de contratos (especialmente para APIs públicas),
a interface costuma ficar mais direta.
Se você quer “montar” tipos a partir de peças (ex.: interseções e mapeamentos), type tende a ser mais expressivo.

3) Merging (declarações que se juntam): o diferencial clássico das interfaces

Uma das diferenças mais conhecidas é que interfaces suportam declaration merging.
Ou seja: você pode declarar a mesma interface em lugares diferentes e o TypeScript “une” as propriedades.

Isso é especialmente útil quando você quer:

  • estender tipos de forma incremental;
  • aprimorar definições de bibliotecas (em padrões de augmentação);
  • organizar contratos por módulo sem criar um tipo “grande” no mesmo arquivo.

type não faz merging. Se você declarar o mesmo alias duas vezes, é erro.
Essa característica influencia como você estrutura seu projeto.

4) Tipos avançados: type tem mais liberdade

type é um “alias” para qualquer expressão de tipo.
Na prática, isso significa que ele se encaixa naturalmente com construções avançadas:
uniões, interseções, tipos condicionais, mapeamentos e inferência.

Interfaces também podem representar objetos, mas têm menos liberdade para expressar lógica de tipos.
Se você precisa modelar estados ou formas condicionais, type tende a ser a escolha mais natural.

Regra mental simples

Use interface quando seu objetivo for contratos extensíveis e “negociáveis” ao longo do projeto.
Use type quando precisar de composição avançada (uniões/interseções) ou modelagem mais expressiva.

Exemplo completo: escolha entre interface e type na prática

Abaixo eu mostro:
(1) interface com extensão,
(2) type com interseção/unidão para modelar estados,
(3) uma ideia de “merging” conceitual para interfaces.

Exemplo: contratos, estados e composição
.ts
type ID = string | number;

interface BaseUser {
  id: ID;
  email: string;
}

interface AdminUser extends BaseUser {
  role: "admin";
  permissions: string[];
}

// Estado do request usando type (união + tipos específicos)
type RequestState =
  | { status: "idle" }
  | { status: "loading"; startedAt: number }
  | { status: "success"; data: unknown }
  | { status: "error"; message: string };

// Composição de tipos com interseção (&)
type Paginated<T> = {
  page: number;
  pageSize: number;
  items: T[];
};

type UserListResult = Paginated<AdminUser> & {
  total: number;
};

// Uso
const admin: AdminUser = {
  id: 1,
  email: "dev@yurideveloper.com",
  role: "admin",
  permissions: ["users:read", "users:write"],
};

const state: RequestState = { status: "loading", startedAt: Date.now() };

const result: UserListResult = {
  page: 1,
  pageSize: 10,
  items: [admin],
  total: 23,
};

// Observação sobre merging:
// Se você declarar "interface BaseUser" em outro arquivo com propriedades adicionais,
// o TypeScript pode mesclar as definições (dependendo do cenário e do nome).]

Nota: o merging real costuma aparecer em augmentação por módulo ou em cenários específicos de organização.
O ponto aqui é entender a propriedade “declaration merging” como capacidade das interfaces, não como algo que você usa do mesmo jeito que type.

Resumo objetivo: como decidir em projetos reais

Se eu tiver que decidir rapidamente, eu penso assim:

  • Prefira interface quando o contrato faz parte de uma API e tende a ser estendido e negociado com clareza.
    Além disso, interfaces ajudam muito quando você quer aproveitar declaration merging.
  • Prefira type quando você precisa de uniões/interseções para modelar estados, fluxos e estruturas condicionais.
    Ele também é a opção mais natural para tipos avançados.
  • Consistência vence: em um projeto, padronize e evite misturar critérios sem motivo.
    O custo de leitura aumenta quando cada arquivo decide sozinho.



“`