<h2>O Que São Type Aliases e Interfaces em TypeScript?</h2>
<p>Type aliases e interfaces são duas formas fundamentais de definir tipos customizados em TypeScript. Um <strong>type alias</strong> é uma forma de criar um novo nome para qualquer tipo, enquanto uma <strong>interface</strong> é uma forma de descrever a estrutura de um objeto. Embora pareçam similares à primeira vista, elas possuem diferenças importantes que impactam diretamente na arquitetura e manutenibilidade do seu código.</p>
<p>A confusão entre esses dois conceitos é extremamente comum entre desenvolvedores que estão começando com TypeScript. Neste artigo, você aprenderá não apenas as diferenças técnicas, mas quando usar cada uma delas em situações reais de desenvolvimento. A escolha correta entre type alias e interface pode fazer a diferença entre um código limpo, extensível e um código difícil de manter.</p>
<h2>Type Aliases: Flexibilidade e Amplitude</h2>
<h3>O Conceito Fundamental</h3>
<p>Um type alias é criado com a palavra-chave <code>type</code> e pode representar qualquer tipo TypeScript: primitivos, união de tipos, tuplas, funções ou objetos. Ele é particularmente útil quando você precisa dar um nome significativo a um tipo complexo ou quando precisa reutilizar uma combinação de tipos em múltiplos lugares.</p>
<pre><code class="language-typescript">// Type alias para um tipo primitivo
type ID = string | number;
// Type alias para uma tupla
type Coordenada = [number, number];
// Type alias para uma função
type Callback = (data: string) => void;
// Type alias para um objeto
type Usuario = {
nome: string;
email: string;
idade: number;
};
// Type alias para uma união de tipos
type Resultado = { sucesso: true; dados: unknown } | { sucesso: false; erro: string };</code></pre>
<p>A flexibilidade é a marca registrada dos type aliases. Você pode criar tipos para praticamente qualquer coisa em TypeScript, o que os torna extremamente versáteis.</p>
<h3>Casos de Uso Práticos</h3>
<p>Type aliases brilham em cenários onde você precisa trabalhar com tipos complexos e compostos. Um exemplo real é quando você está construindo uma camada de resposta de API que pode retornar sucesso ou erro. Usando type alias com tipos discriminados (discriminated unions), você obtém um código muito mais seguro e expressivo.</p>
<pre><code class="language-typescript">type HttpResponse<T> =
{ status: 'success'; statusCode: 200 | 201; data: T } | { status: 'error'; statusCode: 400 | 401 | 500; message: string };
function processarResposta<T>(response: HttpResponse<T>): void {
if (response.status === 'success') {
console.log('Dados:', response.data);
} else {
console.log('Erro:', response.message);
}
}
const respostaSucesso: HttpResponse<{ id: number }> = {
status: 'success',
statusCode: 201,
data: { id: 1 }
};
const respostaErro: HttpResponse<never> = {
status: 'error',
statusCode: 400,
message: 'Dados inválidos'
};</code></pre>
<p>Outro caso prático é quando você precisa trabalhar com tipos genéricos complexos, especialmente em bibliotecas e abstrações de alto nível. Type aliases permitem criar tipos parametrizados que se comportam como templates, oferecendo uma flexibilidade que interfaces não conseguem alcançar da mesma forma.</p>
<h2>Interfaces: Contrato e Extensibilidade</h2>
<h3>O Conceito e Sua Força</h3>
<p>Uma interface define um contrato explícito para a estrutura de um objeto. Ela utiliza a palavra-chave <code>interface</code> e é especificamente projetada para descrever a forma de objetos. Diferentemente de type aliases, interfaces são orientadas a objetos e suportam herança através da palavra-chave <code>extends</code>.</p>
<pre><code class="language-typescript">interface Usuario {
nome: string;
email: string;
idade: number;
}
interface Admin extends Usuario {
permissoes: string[];
nivel: 'super' | 'moderador';
}
// Uma classe implementando a interface
class UsuarioAdmin implements Admin {
nome: string;
email: string;
idade: number;
permissoes: string[];
nivel: 'super' | 'moderador';
constructor(nome: string, email: string, idade: number) {
this.nome = nome;
this.email = email;
this.idade = idade;
this.permissoes = [];
this.nivel = 'moderador';
}
}</code></pre>
<p>A grande vantagem aqui é a clareza de intenção. Quando alguém vê uma interface, sabe imediatamente que está olhando para um contrato que descreve a forma de um objeto. Além disso, interfaces suportam herança múltipla de forma explícita e elegante.</p>
<h3>Casos de Uso em Aplicações Reais</h3>
<p>Interfaces são ideais quando você está construindo uma camada de domínio ou modelo de dados com herança clara. Em uma aplicação de e-commerce, por exemplo, você pode usar interfaces para representar a hierarquia de produtos:</p>
<pre><code class="language-typescript">interface Produto {
id: number;
nome: string;
preco: number;
estoque: number;
}
interface ProdutoFisico extends Produto {
peso: number;
dimensoes: {
altura: number;
largura: number;
profundidade: number;
};
}
interface ProdutoDigital extends Produto {
urlDownload: string;
tamanhoArquivo: number;
}
// Outro caso real: interfaces para dependency injection e padrão Repository
interface RepositorioProduto {
buscarPorId(id: number): Promise<Produto | null>;
buscarTodos(): Promise<Produto[]>;
salvar(produto: Produto): Promise<Produto>;
deletar(id: number): Promise<void>;
}
class RepositorioProdutoSQL implements RepositorioProduto {
async buscarPorId(id: number): Promise<Produto | null> {
// implementação com SQL
return null;
}
async buscarTodos(): Promise<Produto[]> {
// implementação com SQL
return [];
}
async salvar(produto: Produto): Promise<Produto> {
// implementação com SQL
return produto;
}
async deletar(id: number): Promise<void> {
// implementação com SQL
}
}</code></pre>
<p>Neste exemplo, interfaces definem contratos claros que diferentes implementações podem seguir. Isso é fundamental para padrões como Dependency Injection e Strategy Pattern.</p>
<h2>Diferenças Práticas Fundamentais</h2>
<h3>Herança, Fusão e Comportamentos Distintos</h3>
<p>A diferença mais importante entre type alias e interface é como elas lidam com herança e extensão. Interfaces usam <code>extends</code> para herança simples e clara. Type aliases não suportam herança da mesma forma, mas podem ser combinados usando <code>&</code> (intersection types). Além disso, <strong>interfaces podem ser declaradas múltiplas vezes com o mesmo nome, e o TypeScript as funde automaticamente</strong>. Type aliases não permitem isso.</p>
<pre><code class="language-typescript">// Interface: pode ser declarada múltiplas vezes
interface Configuracao {
timeout: number;
}
interface Configuracao {
retentativas: number;
}
// TypeScript funde as duas declarações
const config: Configuracao = {
timeout: 5000,
retentativas: 3
};
// Type alias: você precisa usar intersection types
type ConfigBase = {
timeout: number;
};
type ConfigRetentativa = {
retentativas: number;
};
type Configuracao2 = ConfigBase & ConfigRetentativa;
const config2: Configuracao2 = {
timeout: 5000,
retentativas: 3
};</code></pre>
<p>Outra diferença crucial é que <strong>type aliases podem representar tipos primitivos, uniões e tuplas</strong>, enquanto interfaces só podem descrever objetos. Se você precisa criar um tipo para uma string literal ou uma função, você <em>deve</em> usar type alias.</p>
<pre><code class="language-typescript">// Isso NÃO é possível com interface
type Status = 'pendente' | 'processando' | 'concluido'; type MaiusculaOuMinuscula = Uppercase<string> | Lowercase<string>;
type ValidadorNumero = (valor: number) => boolean;
// Isso SÓ é possível com interface (declaração de fusão)
interface EventEmitter {
on(event: string, listener: (...args: unknown[]) => void): void;
}
interface EventEmitter {
emit(event: string, ...args: unknown[]): void;
}
// Agora EventEmitter tem tanto on quanto emit</code></pre>
<h3>Performance e Compilação</h3>
<p>De um ponto de vista de performance em tempo de compilação, interfaces são ligeiramente mais otimizadas porque o compilador TypeScript consegue processar herança de interface de forma mais direta. Type aliases com intersection types requerem um pouco mais de processamento, especialmente quando aninhados profundamente. Porém, essa diferença é negligenciável em aplicações típicas.</p>
<pre><code class="language-typescript">// Muito aninhado - mais processamento na compilação
type A = { a: number };
type B = A & { b: string };
type C = B & { c: boolean };
type D = C & { d: object };
type E = D & { e: number[] };
// Hierarquia de interface - mais direto para o compilador
interface A {
a: number;
}
interface B extends A {
b: string;
}
interface C extends B {
c: boolean;
}
interface D extends C {
d: object;
}
interface E extends D {
e: number[];
}</code></pre>
<h2>Quando Usar Cada Uma: Guia Prático</h2>
<h3>Escolha Type Alias Quando:</h3>
<ul>
<li>Você precisa representar tipos primitivos, uniões ou tuplas</li>
<li>Você está criando tipos genéricos complexos (como utility types)</li>
<li>Você precisa de discriminated unions para validação de dados</li>
<li>Você está trabalhando com tipos funcionais (tipos de funções)</li>
<li>Você quer criar tipos compostos usando intersection (&)</li>
</ul>
<h3>Escolha Interface Quando:</h3>
<ul>
<li>Você está descrevendo a forma de objetos em sua aplicação</li>
<li>Você precisa de herança clara e explícita entre tipos</li>
<li>Você está criando contratos para classes implementarem</li>
<li>Você quer que seu código aproveite a fusão de declarações</li>
<li>Você está construindo uma arquitetura orientada a objetos</li>
</ul>
<pre><code class="language-typescript">// Exemplo: quando usar cada uma em um projeto real
// Type alias para tipos de dados/validação
type Email = string & { readonly __brand: 'Email' };
type PositivoInteiro = number & { readonly __brand: 'PositivoInteiro' };
type LoginRequest = {
email: Email;
senha: string;
};
type LoginResponse =
{ sucesso: true; token: string } | { sucesso: false; motivo: string };
// Interface para contrato de serviço
interface AuthService {
login(request: LoginRequest): Promise<LoginResponse>;
logout(token: string): Promise<void>;
verificarToken(token: string): Promise<boolean>;
}
// Implementação
class AuthServiceImpl implements AuthService {
async login(request: LoginRequest): Promise<LoginResponse> {
// validar email e senha
return { sucesso: true, token: 'xyz' };
}
async logout(token: string): Promise<void> {
// invalidar token
}
async verificarToken(token: string): Promise<boolean> {
return true;
}
}</code></pre>
<h2>Conclusão</h2>
<p>Os pontos principais que você deve levar desta aula são: <strong>primeiro</strong>, type aliases e interfaces resolvem problemas ligeiramente diferentes — type aliases são sobre criar nomes para tipos complexos e arbitrários, enquanto interfaces definem contratos para objetos; <strong>segundo</strong>, a fusão de declarações de interface é um recurso poderoso e único que não existe com type aliases, permitindo extensão de tipos em múltiplos arquivos; <strong>terceiro</strong>, a escolha entre um e outro deve ser guiada pela intenção: se você precisa descrever a forma de um objeto em uma arquitetura orientada a objetos, use interface; se você precisa de flexibilidade para representar uniões, tipos genéricos ou primitivos, use type alias. Em projetos maiores, é comum usar ambas: interfaces para camadas de domínio e serviço, type aliases para validação, transformação e tipos complexos.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://www.typescriptlang.org/docs/handbook/2/types-from-types.html#type-aliases" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Type Aliases</a></li>
<li><a href="https://www.typescriptlang.org/docs/handbook/2/objects.html" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Interfaces</a></li>
<li><a href="https://basarat.gitbook.io/typescript/type-system/type-aliases" target="_blank" rel="noopener noreferrer">TypeScript Deep Dive - Types vs Interfaces</a></li>
<li><a href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions" target="_blank" rel="noopener noreferrer">Advanced TypeScript - Discriminated Unions</a></li>
<li><a href="https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/" target="_blank" rel="noopener noreferrer">Microsoft TypeScript Blog - Announcing TypeScript 4.0</a></li>
</ul>
<p><!-- FIM --></p>