Type aliases e interfaces: diferenças e quando usar cada um
1. Conceitos Fundamentais
O que são Type Aliases?
Type aliases são definidos com a palavra-chave type e permitem criar nomes para qualquer tipo em TypeScript. Diferentemente das interfaces, eles podem representar tipos primitivos, uniões, interseções e tipos complexos.
// Tipos primitivos
type ID = string | number;
type Status = 'active' | 'inactive' | 'pending';
// Uniões e interseções
type SuccessResponse = { data: unknown; status: 200 };
type ErrorResponse = { error: string; status: 400 | 500 };
type ApiResponse = SuccessResponse | ErrorResponse;
// Tuplas
type Coordinate = [number, number];
type Pair = [string, number];
// Tipos utilitários
type Nullable<T> = T | null;
type ReadonlyUser<T> = { readonly [K in keyof T]: T[K] };
O que são Interfaces?
Interfaces são definidas com a palavra-chave interface e são usadas principalmente para descrever contratos de objetos. Elas são extensíveis naturalmente e suportam declaração de métodos.
interface User {
id: number;
name: string;
email: string;
getFullName(): string;
}
interface Config {
apiUrl: string;
timeout: number;
retries?: number;
}
Semelhanças superficiais
Ambos podem descrever a forma de um objeto e são usados em anotações de tipo:
// Type alias
type PersonType = {
name: string;
age: number;
};
// Interface
interface PersonInterface {
name: string;
age: number;
}
// Ambos funcionam da mesma forma para objetos simples
const person1: PersonType = { name: "Alice", age: 30 };
const person2: PersonInterface = { name: "Bob", age: 25 };
2. Diferenças Estruturais e Semânticas
Extensibilidade e Declaration Merging
Interfaces suportam declaration merging — múltiplas declarações com o mesmo nome são automaticamente mescladas:
interface User {
name: string;
}
interface User {
age: number;
}
// Resultado: User tem name e age
const user: User = { name: "Alice", age: 30 };
Type aliases não permitem redeclaração:
type User = { name: string };
type User = { age: number }; // Erro: identificador duplicado
Suporte a Tipos Não-Objeto
Type aliases são mais flexíveis para tipos que não são objetos:
// Type aliases podem representar qualquer tipo
type Primitive = string | number | boolean;
type Tuple = [string, number];
type MappedType<T> = { [K in keyof T]: string };
// Interfaces são restritas a objetos e funções
interface Callable {
(x: number): string;
}
Herança e Composição
Interfaces usam extends para herança múltipla:
interface BaseUser {
id: number;
name: string;
}
interface Admin extends BaseUser {
role: 'admin';
permissions: string[];
}
// Implementação em classes
class AdminUser implements Admin {
id: number;
name: string;
role: 'admin';
permissions: string[];
}
Type aliases usam interseção (&) para composição:
type BaseUser = { id: number; name: string };
type Admin = BaseUser & { role: 'admin'; permissions: string[] };
3. Casos de Uso Onde Interfaces Brilham
APIs Públicas e Bibliotecas
Interfaces são ideais para APIs públicas devido ao declaration merging:
// Biblioteca
export interface HttpClient {
get(url: string): Promise<Response>;
post(url: string, data: unknown): Promise<Response>;
}
// Consumidor pode estender
interface HttpClient {
baseUrl: string;
}
Contratos de Objetos Complexos
Hierarquias claras com extends:
interface Animal {
name: string;
age: number;
}
interface Mammal extends Animal {
furColor: string;
}
interface Dog extends Mammal {
breed: string;
bark(): void;
}
4. Casos de Uso Onde Type Aliases São Preferíveis
Tipos Compostos e Uniões
type Status = 'active' | 'inactive' | 'pending';
type Result<T> = { success: true; data: T } | { success: false; error: string };
function processResult(result: Result<number>) {
if (result.success) {
console.log(result.data); // number
} else {
console.error(result.error); // string
}
}
Tipos Utilitários e Mapeados
type PartialConfig<T> = { [K in keyof T]?: T[K] };
type Readonly<T> = { readonly [K in keyof T]: T[K] };
type Nullable<T> = T | null;
type FunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? K : never
}[keyof T];
5. Limitações e Armadilhas Comuns
Type Aliases: Sem Declaration Merging
Para bibliotecas que precisam de extensão por terceiros, isso pode ser limitante:
// Em uma biblioteca
type Config = { apiUrl: string };
// Consumidor não pode estender automaticamente
type ExtendedConfig = Config & { timeout: number }; // Solução manual
Interfaces: Incapacidade de Representar Uniões
// Isto não funciona com interfaces
interface Result = Success | Error; // Erro de sintaxe
// Solução com type
type Result = Success | Error;
6. Guia Prático de Decisão
Use Interfaces quando:
- Definir contratos para objetos em APIs públicas
- Precisar de declaration merging
- O tipo será implementado por classes
- Criar hierarquias com herança múltipla
Use Type Aliases quando:
- Precisar de uniões, interseções ou tuplas
- Criar tipos utilitários genéricos
- Trabalhar com tipos primitivos
- O tipo for complexo e não se encaixar em objeto simples
Regra de Ouro da Comunidade
Comece com interface para objetos, troque para type se precisar de uniões. Seja consistente dentro do mesmo projeto.
7. Exemplos Comparativos e Boas Práticas
Refatoração de Interface para Type
// Antes: Interface limitante
interface ApiResponse {
data: unknown;
error?: string;
status: number;
}
// Depois: Type mais flexível
type ApiResponse<T> =
| { success: true; data: T; status: 200 }
| { success: false; error: string; status: 400 | 500 };
Combinação de Ambos
interface User {
name: string;
email: string;
}
type AdminUser = User & { role: 'admin' };
type ReadonlyUser = Readonly<User>;
Recomendações Finais
- Prefira interfaces em código público e types em código interno complexo
- Evite misturar estilos sem critério claro
- Documente a escolha no guia de estilo da equipe
- Lembre-se: a escolha entre type e interface é mais sobre semântica e manutenibilidade do que funcionalidade
Referências
- TypeScript Handbook: Object Types — Documentação oficial sobre interfaces e type aliases para objetos
- TypeScript Handbook: Type Aliases — Seção oficial sobre type aliases com exemplos práticos
- TypeScript Deep Dive: Type Aliases vs Interfaces — Guia detalhado comparando type aliases e interfaces por Basarat Ali Syed
- TypeScript Evolution: Interfaces vs Types — Artigo técnico de Marius Schulz sobre as diferenças e evolução do TypeScript
- TypeScript Official Playground — Ambiente interativo oficial para testar type aliases, interfaces e verificar erros de tipo em tempo real
- Stack Overflow: TypeScript Interfaces vs Types — Discussão comunitária abrangente sobre as diferenças práticas e casos de uso