Type Alias no TypeScript: Guia Prático
Type alias vai além de dar nomes a tipos. Union types, intersection, literal types e composição. Tudo com exemplos que você aplica agora.
Por que isso é importante
Type Alias no TypeScript: Guia Prático. Type alias vai além de dar nomes a tipos. Union types, intersection, literal types e composição. Tudo com exemplos que você aplica agora.
O Que É Type Alias no TypeScript
Type alias é um nome customizado para qualquer tipo no TypeScript. Pode ser simples como dar um nome pra string, ou complexo como criar uma combinação de tipos condicionais, unions e intersections. A flexibilidade é absurda.
A diferença entre type e interface é sutil mas importante. Interface foi feita pra descrever a forma de objetos. Type alias foi feito pra tudo: primitivos, unions, tuplas, funções, tipos condicionais, template literals. É mais flexível e cobre cenários que interface simplesmente não consegue.
Um detalhe que confunde muita gente: type não cria um novo tipo. Ele cria um apelido. type ID = string não inventa um tipo novo, só dá o nome ID pro tipo string. Qualquer lugar que aceita string aceita ID e vice-versa. É transparente pro compilador.
Mas esse "apelido" tem poder imenso. Dá pra compor tipos complexos, extrair tipos de objetos, criar tipos condicionais e muito mais. Quando você domina type alias, desbloqueia um nível de tipagem que transforma seu código.
Na prática, type alias e interface se complementam. A convenção mais aceita é: use interface pra objetos e classes, use type pra todo o resto. Mas não se estresse com isso no começo. Os dois resolvem o problema e o TypeScript aceita ambos na maioria das situações.
Como Usar Type Alias Passo a Passo
Cada passo abre uma possibilidade nova. Do mais simples ao mais sofisticado, vamos construir seu repertório de tipagem avançada.
Exemplos Práticos de Type Alias
Vamos ver type alias em cenários que você encontra em qualquer projeto real. Cada exemplo mostra um pattern diferente.
Union Types: Valores Restritos
// Status de pedido - só aceita esses valores
type StatusPedido = "pendente" | "pago" | "enviado" | "entregue" | "cancelado";
function atualizarPedido(id: number, status: StatusPedido): void {
console.log(`Pedido ${id} atualizado para: ${status}`);
}
atualizarPedido(1, "pago"); // OK
atualizarPedido(2, "enviado"); // OK
// atualizarPedido(3, "devolvido"); // Error! "devolvido" não está no union
// Union com tipos diferentes
type Resposta = string | number | boolean;
function processar(valor: Resposta): string {
if (typeof valor === "string") return `Texto: ${valor}`;
if (typeof valor === "number") return `Número: ${valor}`;
return `Boolean: ${valor}`;
}
Union types de strings são tão úteis que substituem enums na maioria dos casos. A vantagem sobre enum é que não gera código JavaScript extra e funciona melhor com autocomplete. Quando alguém digita status = ", o editor já mostra todas as opções disponíveis. Produtividade pura.
Intersection Types: Composição de Tipos
// Tipos base
type DadosPessoais = {
nome: string;
cpf: string;
dataNascimento: Date;
};
type DadosProfissionais = {
cargo: string;
empresa: string;
salario: number;
};
type Endereco = {
rua: string;
cidade: string;
estado: string;
cep: string;
};
// Intersection: combina todos os tipos
type FuncionarioCompleto = DadosPessoais & DadosProfissionais & Endereco;
const funcionario: FuncionarioCompleto = {
nome: "Ana Paula",
cpf: "123.456.789-00",
dataNascimento: new Date("1990-05-15"),
cargo: "Tech Lead",
empresa: "StartupXYZ",
salario: 18000,
rua: "Av. Paulista, 1000",
cidade: "São Paulo",
estado: "SP",
cep: "01310-000",
};
Intersection é composição. Você monta tipos complexos a partir de peças menores e reutilizáveis. DadosPessoais pode ser usado sozinho em formulários de cadastro. DadosProfissionais pode ser usado no módulo de RH. E FuncionarioCompleto combina os dois quando precisa de tudo junto. Reutilização de tipos elimina duplicação.
Literal Types e Discriminated Unions
// Literal types: valores exatos
type Tema = "claro" | "escuro" | "sistema";
type Tamanho = "P" | "M" | "G" | "GG";
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
// Discriminated union - cada tipo tem um campo discriminador
type Notificacao =
| { tipo: "email"; destinatario: string; assunto: string; corpo: string }
| { tipo: "sms"; telefone: string; mensagem: string }
| { tipo: "push"; token: string; titulo: string; corpo: string };
function enviarNotificacao(notif: Notificacao): void {
switch (notif.tipo) {
case "email":
console.log(`Email para ${notif.destinatario}: ${notif.assunto}`);
break;
case "sms":
console.log(`SMS para ${notif.telefone}: ${notif.mensagem}`);
break;
case "push":
console.log(`Push para ${notif.token}: ${notif.titulo}`);
break;
}
}
Discriminated unions são um dos patterns mais poderosos do TypeScript. O campo tipo funciona como discriminador: dentro de cada case do switch, o TypeScript sabe exatamente quais propriedades existem. Sem casts, sem verificações manuais. O compilador faz o narrowing automaticamente. Isso é tipagem inteligente de fato.
Tuplas e Function Types
// Tuplas: arrays com tipos por posição
type Coordenada = [number, number];
type NomeIdade = [string, number];
type ResultadoAPI = [boolean, string, number]; // sucesso, mensagem, código
const ponto: Coordenada = [10, 20];
const pessoa: NomeIdade = ["João", 28];
const resultado: ResultadoAPI = [true, "OK", 200];
// Destructuring tipado
const [sucesso, mensagem, codigo] = resultado;
// sucesso: boolean, mensagem: string, codigo: number
// Function types com type alias
type Validador = (valor: string) => boolean;
type AsyncHandler = (req: Request, res: Response) => Promise<void>;
const validarEmail: Validador = (email) => {
return email.includes("@") && email.includes(".");
};
console.log(validarEmail("test@email.com")); // true
console.log(validarEmail("invalido")); // false
Tuplas são arrays com superpoderes. Cada posição tem um tipo específico e o tamanho é fixo. Quando você desestrutura, cada variável já tem o tipo certo. Perfeito pra retornos de funções onde um array faz mais sentido que um objeto (como o useState do React que retorna [valor, setter]).
Template Literal Types
// Combinar literal types com template strings
type Cor = "vermelho" | "azul" | "verde";
type Tonalidade = "claro" | "escuro";
// Gera: "vermelho-claro" | "vermelho-escuro" | "azul-claro" ...
type CorCompleta = `${Cor}-${Tonalidade}`;
const cor: CorCompleta = "azul-escuro"; // OK
// const errada: CorCompleta = "amarelo-claro"; // Error!
// Muito usado em CSS-in-JS e APIs
type Evento = "click" | "hover" | "focus";
type Handler = `on${Capitalize<Evento>}`; // "onClick" | "onHover" | "onFocus"
Template literal types geram combinações automaticamente. Se Cor tem 3 valores e Tonalidade tem 2, CorCompleta tem 6 valores. O TypeScript calcula todas as combinações possíveis. Isso é extremamente útil em design systems, APIs com padrões de nomenclatura e CSS-in-JS. Usa com moderação: se as combinações explodirem, o compilador fica lento.
Quando Usar Type vs Interface
A regra prática é direta. Use interface quando vai modelar a forma de um objeto ou classe. Use type quando precisa de union, intersection, tupla, tipo primitivo nomeado ou qualquer composição que interface não suporta.
Interface suporta declaration merging: se você declara a mesma interface duas vezes, o TypeScript combina as propriedades. Isso é importante pra bibliotecas que precisam ser estendidas. Type alias não suporta isso. Uma vez declarado, não dá pra adicionar propriedades depois.
Na dúvida, comece com interface pra objetos e troque pra type quando encontrar uma limitação. Muitos times adotam a convenção de interface pra tudo e só usam type quando precisa de union. Tanto faz, desde que o time todo siga a mesma convenção. Consistência importa mais que a escolha em si.
Erros Comuns com Type Alias
Armadilhas que travam devs
Usar type pra tudo, inclusive objetos simples: se o tipo descreve a forma de um objeto e pode precisar de extends ou declaration merging no futuro, interface é mais adequada. Type é melhor pra unions, primitivos e composições.
Criar unions gigantes sem discriminador: union de 10 objetos sem um campo comum que identifique cada tipo vira pesadelo. Sempre adicione um campo tipo ou kind como discriminador. Sem ele, o narrowing dentro de funções é manual e propenso a erro.
Esquecer que intersection combina, não escolhe: type A & B significa que o objeto precisa ter TODAS as propriedades de A E B. Não é "A ou B". Pra isso, use union (A | B). Confundir & com | é um dos erros mais comuns entre iniciantes.
Não reutilizar type alias: se você repete string | number | null em 10 lugares, crie type Nullable<T> = T | null e reutilize. Copiar o mesmo union em vários arquivos é dívida técnica que cresce rápido.
Template literal types complexos demais: são poderosos mas podem gerar tipos com centenas de combinações. Use com moderação pra não sobrecarregar o compilador e não confundir quem lê o código depois.
Checklist de Type Alias
Tipos Avançados na Prática
Type alias é só a porta de entrada pra tipagem avançada no TypeScript. No CrazyStack, você usa unions, intersections, generics e utility types num projeto real com Node.js e React. Não é código de playground: é SaaS com autenticação, pagamento e deploy.
Cada decisão de tipo no curso tem contexto. Você entende o porquê, não só o como. É o que separa quem sabe TypeScript de quem sabe usar TypeScript de fato.
Continue lendo
Como Usar Interface no TypeScript: Guia Prático
Crie contratos de dados sólidos com interfaces no TypeScript.
Diferença entre Type e Interface no TypeScript
Entenda quando usar type e quando usar interface no TypeScript.
Como Usar Union Types no TypeScript
Domine union types para criar tipagens flexíveis e seguras.
Generics no TypeScript
Tipos reutilizáveis