Union Types no TypeScript: Guia Prático
Union types são uma das ferramentas mais poderosas do TypeScript. Com o operador |, você combina tipos e cria código flexível sem abrir mão da segurança de tipagem.
Por que isso é importante
Union Types no TypeScript: Guia Prático. Union types são uma das ferramentas mais poderosas do TypeScript. Com o operador |, você combina tipos e cria código flexível sem abrir mão da segurança de tipagem. Aprenda narrowing, type guards e discriminated unions na prática.
O que são Union Types e quando usar
Union type nada mais é do que dizer: essa variável aceita tipo A ou tipo B. A sintaxe usa o operador <code>|</code> (pipe). Simples assim. Você combina dois ou mais tipos, e o TypeScript garante que só operações válidas para todos os tipos da union passem pelo compilador.
O uso mais direto é quando uma função recebe parâmetros que podem ter formatos diferentes. Por exemplo, um ID que pode ser <code>string</code> ou <code>number</code>. Ou o status de um pedido que só aceita valores específicos. Union types cobrem esses cenários sem nenhuma gambiarra.
Dá pra usar union types em variáveis, parâmetros de função, retornos, propriedades de objetos e até dentro de generics. A flexibilidade é enorme. Mas o poder de verdade aparece quando você combina union types com narrowing -- o processo de afunilar o tipo dentro de um bloco de código.
// Union type simples
type ID = string | number;
let userId: ID = "abc123";
userId = 42; // Tudo certo, aceita os dois
// Union com literais
type Status = "ativo" | "inativo" | "pendente";
let statusUsuario: Status = "ativo"; // OK
// statusUsuario = "banido"; // Erro! Não faz parte da union
Passo a passo: do básico ao avançado
type Resultado = string | number. Pode ser com types primitivos, interfaces, objetos literais ou até outras unions.if ou switch, o TypeScript afunila automaticamente o tipo. Depois do check, você acessa métodos exclusivos de cada tipo sem erro.is no retorno: function isString(val: unknown): val is string. Isso dá controle total.type: "sucesso") em cada variante. O TypeScript usa essa prop pra diferenciar os tipos automaticamente.Result<T> = Success<T> | Error. Padrão usado em APIs, hooks e serviços.Exemplos práticos: narrowing e discriminated unions
Narrowing com typeof
O narrowing mais básico usa <code>typeof</code>. O TypeScript entende que, dentro do bloco <code>if (typeof x === "string")</code>, a variável é string. Fora do bloco, continua sendo a union completa.
function formatarID(id: string | number): string {
if (typeof id === "string") {
return id.toUpperCase(); // TypeScript sabe que é string aqui
}
return id.toFixed(0); // Aqui sabe que é number
}
console.log(formatarID("abc")); // "ABC"
console.log(formatarID(42)); // "42"
Discriminated Unions com objetos
Galera, discriminated unions são o padrão mais usado em projetos reais. Você coloca uma propriedade literal que serve como "etiqueta" pra cada variante. O TypeScript usa essa etiqueta pra inferir o tipo exato dentro de um <code>switch</code> ou <code>if</code>.
type Sucesso = {
status: "sucesso";
dados: string[];
};
type Falha = {
status: "erro";
mensagem: string;
};
type Resposta = Sucesso | Falha;
function tratarResposta(res: Resposta) {
switch (res.status) {
case "sucesso":
// TypeScript sabe que res é Sucesso aqui
console.log(res.dados.join(", "));
break;
case "erro":
// TypeScript sabe que res é Falha aqui
console.log(`Erro: ${res.mensagem}`);
break;
}
}
Type Guard customizado
Quando trabalha com classes ou objetos sem propriedade discriminante, dá pra criar type guards com a keyword <code>is</code>. A função retorna um booleano, mas o TypeScript usa a anotação pra refinar o tipo.
interface Gato {
miar(): void;
}
interface Cachorro {
latir(): void;
}
type Pet = Gato | Cachorro;
function isGato(pet: Pet): pet is Gato {
return (pet as Gato).miar !== undefined;
}
function fazerBarulho(pet: Pet) {
if (isGato(pet)) {
pet.miar(); // TypeScript sabe que é Gato
} else {
pet.latir(); // TypeScript sabe que é Cachorro
}
}
Erros comuns com Union Types
Cuidado com esses deslizes
Acessar propriedade sem narrowing: se a variável é string | number, você não pode chamar .toUpperCase() direto. O TypeScript exige que você afunile o tipo antes.
Confundir union com intersection: A | B significa A ou B. A & B significa A e B ao mesmo tempo. A diferença é enorme e gera bugs difíceis de rastrear quando trocadas.
Esquecer de tratar todos os casos: quando usa discriminated unions, cada variante precisa ser coberta. Se adicionar um novo status e esquecer o case no switch, vai ter surpresa em produção.
Usar any ao invés de union: se o parâmetro aceita string ou number, declare string | number. Colocar any desliga toda proteção do compilador -- perde o sentido de usar TypeScript.
Não usar exhaustive check: no final do switch, adicione um default com never pra garantir que novas variantes sejam tratadas em tempo de compilação.
Checklist: Union Types bem aplicados
Checklist de Union Types
Domine TypeScript na prática
Union types são só o começo. No CrazyStack, você aprende TypeScript construindo projetos reais com Node.js e React -- cada conceito aplicado em código que vai pro ar. Chega de tutorial solto: monte seu projeto do zero ao deploy com tipagem forte do início ao fim.