Union types: isto ou aquilo, nunca um terceiro
Muitos valores do mundo real não são um único tipo fixo. Um id pode ser texto ou número; um status tem um conjunto fechado de opções válidas.
Estudar esta lição interativa →Alguns valores realmente têm um único tipo possível: uma idade é sempre number, um nome é sempre string. Mas outros valores, no mundo real, aceitam mais de um tipo, sem por isso aceitarem qualquer coisa. Um id de pedido pode vir como número (direto do banco de dados) ou como texto (numa URL compartilhável), por exemplo, mas nunca deveria virar um booleano ou um objeto qualquer.
Para descrever exatamente essa ideia, "ou um tipo, ou outro, nunca um terceiro", existe o union type (tipo de união), escrito com uma barra vertical entre os tipos aceitos:
// Union type: o valor pode ser DE UM TIPO OU DE OUTRO, nunca de um terceiro.
let id: string | number;
id = "abc123"; // ok
id = 42; // também ok
// id = true; // erro: boolean não está na união
console.log(id);Pense num union type como uma porta com duas fechaduras diferentes, e qualquer uma das duas chaves abre: a chave string ou a chave number. Qualquer outra chave, como uma boolean, simplesmente não serve naquela porta.
Uma variação poderosa do union type usa, no lugar de tipos inteiros como string ou number, valores exatos entre aspas. São os tipos literais: em vez de "qualquer texto", você trava a variável a um conjunto fechado e pequeno de textos válidos. É extremamente comum para representar o status de algo, como um pedido de loja:
// Tipos literais: em vez do tipo inteiro (string), você trava os valores
// possíveis num conjunto fechado. É a base de um pedido de e-commerce real.
let statusPedido: "pendente" | "pago" | "cancelado" = "pendente";
statusPedido = "pago"; // ok, está na lista
// statusPedido = "enviado"; // erro: "enviado" não é um dos três valores aceitos
console.log(statusPedido);Repare na diferença de rigor. Um status: string aceitaria literalmente qualquer texto, inclusive um "enviadoo" com erro de digitação, um "talvez" sem sentido nenhum para o seu sistema, qualquer coisa. Já status: "pendente" | "pago" | "cancelado" só aceita, letra por letra, um desses três valores exatos. É a diferença entre "qualquer texto" e "só estas opções específicas que fazem sentido para o meu programa".
Uma confusão comum: string | number não significa que o valor precisa satisfazer os dois tipos simultaneamente. Significa que, em qualquer momento, ele é um dos dois, nunca os dois ao mesmo tempo e nunca um terceiro tipo diferente. (Isso vale para primitivos como esses; numa união de objetos, um objeto que tem os campos dos dois formatos também cabe.)
// Sua vez! O status abaixo devia aceitar só "livre" ou "ocupada" (uma mesa
// de restaurante), mas está tipado como string solto, o que deixa passar
// qualquer texto. Troque a anotação para a união de literais correta.
let mesa: string = "livre";
mesa = "reservada"; // deveria ser barrado, mas hoje passa
console.log(mesa);Troque para a união de literais correta.