TypeScript: Tipos de união e tipos literais no TypeScript

Última atualização: 2026-08-26

Os tipos de união e os tipos literais são o primeiro passo no avanço do TypeScript em relação ao JavaScript — eles permitem expressar que “esse valor pode ser A ou B” e que “esse valor só pode ser uma dessas opções”.

1. Tipos de união

Um tipo de união utiliza | para combinar vários tipos, indicando que “o valor pode ser qualquer um desses tipos”:

(1) Sintaxe básica

TYPESCRIPT
let id: number | string;

id = 42;        // ✅ number Legal
id = "ABC";     // ✅ string Legal
id = true;      // ❌ boolean Not in a composite type

(2) Usos comuns: parâmetros de função

O uso mais comum dos tipos de união é permitir que as funções aceitem parâmetros de vários tipos:

TYPESCRIPT
function printId(id: number | string) {
  console.log("YourID: :" + id);
}

printId(42);       // ✅
printId("ABC");    // ✅
printId(true);     // ❌

(3) Restrições aos tipos de união

Variáveis de um tipo de união só podem acessar membros comuns a todos os tipos:

TYPESCRIPT
function process(value: number | string) {
  // value.toString()   // ✅ number and string All of them toString
  // value.toUpperCase() // ❌ number None toUpperCase
  // value.toFixed(2)    // ❌ string None toFixed
}
📌 Motivo: O TypeScript deve garantir que o código permaneça seguro mesmo quando value for qualquer membro da união. Apenas propriedades e métodos compartilhados por todos os membros podem ser chamados diretamente. Para acessar um membro de um tipo específico, é preciso primeiro usar o “estreitamento de tipo” (consulte a Seção 3).



2. Tipos literais

Os tipos literais restringem o valor de uma variável a literais específicos — não “qualquer string”, mas “apenas essas strings específicas”.

(1) Três tipos de literais

Tipos literais Sintaxe Exemplo
Literal de string "value1" | "value2" "north" | "south" | "east" | "west"
Literais numéricos 1 | 2 | 3 0 | 1 | 2
Literal booleano true | false É realmente igual a boolean (apenas dois valores)

(2) Tipos de literais de string

TYPESCRIPT
let direction: "north" | "south" | "east" | "west";

direction = "north";    // ✅
direction = "up";       // ❌ "up" Not in a composite type

(3) Tipos de literais numéricos

TYPESCRIPT
type HttpStatusCode = 200 | 301 | 404 | 500;

let code: HttpStatusCode = 200;   // ✅
let code2: HttpStatusCode = 201;  // ❌ 201 Not in a composite type

(4) Tipos literais booleanos

TYPESCRIPT
type YesNo = true | false;  // equivalent to boolean
type StrictTrue = true;     // It can only be true
💡 Dica: O tipo literal booleano não tem muito significado por si só (true | false é simplesmente um booleano), mas é útil quando combinado com outros tipos em tipos de união: string | true representa “uma string ou verdadeiro”.

▶ Exemplo: Definindo permissões de função usando tipos literais

TYPESCRIPT
type Role = "admin" | "editor" | "viewer";

function checkPermission(role: Role) {
  if (role === "admin") {
    console.log("Administrator:Full Access");
  } else if (role === "editor") {
    console.log("Editor:Editable Content");
  } else {
    console.log("Viewers:Read-only access");
  }
}

checkPermission("admin");   // ✅
checkPermission("editor");  // ✅
checkPermission("guest");   // ❌ Compilation Error——"guest" Not legal Role
▶ Experimente

Saída:

TEXT 📖 Somente leitura
Administrator:Full Access
Editor:Editable Content


3. typenarrowing (restrição de tipos)

Variáveis de um tipo de união não podem acessar diretamente membros de um tipo específico, mas o escopo pode ser restringido por meio do “reestreitamento de tipo”.

(1) restrição de tipo com typeof

TYPESCRIPT
function process(value: number | string) {
  if (typeof value === "string") {
    // In this branch,TypeScript knows value is string
    console.log(value.toUpperCase());  // ✅ string Methods
  } else {
    // In this branch,TypeScript knows value is number
    console.log(value.toFixed(2));     // ✅ number Methods
  }
}

(2) Restrição da igualdade

TYPESCRIPT
function compare(a: string | number, b: string | boolean) {
  if (a === b) {
    // a and b equal,Their intersection type is string
    console.log(a.toUpperCase());  // ✅ Here a It must be string
  }
}

(3) Restrição do operador in

TYPESCRIPT
function process(input: { name: string } | { age: number }) {
  if ("name" in input) {
    console.log(input.name);  // ✅ Has name property,Inferred as { name: string }
  } else {
    console.log(input.age);   // ✅ None name,Inferred as { age: number }
  }
}

(4) restrição do instanceof

TYPESCRIPT
function processDate(value: Date | string) {
  if (value instanceof Date) {
    console.log(value.toISOString());  // ✅ Date Methods
  } else {
    console.log(value.toUpperCase());  // ✅ string Methods
  }
}

▶ Exemplo: Uma demonstração completa do estreitamento de tipos

TYPESCRIPT
function formatValue(value: number | string | boolean): string {
  if (typeof value === "number") {
    return "Numbers:" + value.toFixed(2);
  } else if (typeof value === "string") {
    return "String:" + value.toUpperCase();
  } else {
    return "Boolean:" + value;
  }
}

console.log(formatValue(3.14159));
console.log(formatValue("hello"));
console.log(formatValue(true));
▶ Experimente

Saída:

TEXT 📖 Somente leitura
Numbers:3.14
String:HELLO
Boolean:true


4. Padrões clássicos para tipos de união e tipos literais

A combinação de tipos de união e tipos literais pode permitir restrições de tipo muito poderosas. Aqui estão três padrões clássicos:

(1) Padrão 1: União discriminada

Distinguir membros da união usando valores literais diferentes para a mesma propriedade:

TYPESCRIPT
interface Circle {
  kind: "circle";       // Identifiable Attributes
  radius: number;
}

interface Rectangle {
  kind: "rectangle";    // Identifiable Attributes
  width: number;
  height: number;
}

type Shape = Circle | Rectangle;

function getArea(shape: Shape): number {
  if (shape.kind === "circle") {
    return Math.PI * shape.radius ** 2;   // ✅ Accessible radius
  } else {
    return shape.width * shape.height;     // ✅ Accessible width、height
  }
}
📌 Ponto-chave: Cada interface possui uma propriedade kind, mas seu valor é de um tipo literal diferente. O TypeScript usa o valor de kind para restringir o tipo e acessar com segurança as propriedades específicas dessa interface.

(2) Padrão 2: Valores opcionais + indefinido

TYPESCRIPT
type Result = string | undefined;

function search(query: string): Result {
  if (query.length === 0) return undefined;
  return "Found:" + query;
}

let result = search("TypeScript");
if (result !== undefined) {
  console.log(result.toUpperCase());  // ✅ narrowed to string
}

(3) Padrão 3: Máquina de estados

TYPESCRIPT
type RequestStatus = "idle" | "loading" | "success" | "error";

interface RequestState {
  status: RequestStatus;
  data?: string;
  error?: string;
}

function renderState(state: RequestState): string {
  switch (state.status) {
    case "idle":
      return "Waiting......";
    case "loading":
      return "Loading......";
    case "success":
      return "Success:" + state.data;    // ✅ success in that state data Existence
    case "error":
      return "Error:" + state.error;   // ✅ error in that state error Existence
  }
}


5. O tipo never e as verificações exaustivas

Quando o TypeScript se restringe a um “cenário impossível”, o tipo passa a ser never:

TYPESCRIPT
type Shape = "circle" | "square";

function getIcon(shape: Shape): string {
  switch (shape) {
    case "circle": return "⭕";
    case "square": return "⬜";
    default:
      // If all case It's all taken care of.,shape Here, the type is never
      const _exhaustiveCheck: never = shape;  // ✅ If omitted case,An error will occur here
      return _exhaustiveCheck;
  }
}
💡 Dica: O padrão de verificação exaustiva do never — atribuir um valor a uma variável do tipo never no ramo default da instrução switch. Se você adicionar um novo membro a Shape no futuro, mas se esquecer de adicionar um case correspondente, o TypeScript lançará um erro no ramo default, solicitando que você o adicione.

▶ Exemplo: O efeito prático da verificação exaustiva

TYPESCRIPT
type TrafficLight = "red" | "yellow" | "green";

function getAction(light: TrafficLight): string {
  switch (light) {
    case "red": return "Stop";
    case "yellow": return "Note";
    // Intentional omission "green" 's case
    default:
      // If you add the next line,TypeScript It will throw an error:
      // You cannot convert the type "green" Assignment to a Type "never"
      const exhaustive: never = light;
      return "Unknown";
  }
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
// Executed successfully

❓ Perguntas Frequentes

P: Qual é a diferença entre tipos de união e tipos de interseção? R: Um tipo de união (A | B) significa “A ou B” — trata-se da união, portanto, um valor precisa satisfazer apenas um deles. Um tipo de interseção (A & B) significa “tanto A quanto B” — trata-se da interseção, portanto, um valor deve satisfazer ambos. A lição 20 explicará os tipos de interseção em detalhes.

P: O que devo usar: tipos literais ou enums? R: Prefira tipos literais de união. Eles são mais leves que as enums (não é gerado código extra na hora da compilação) e são totalmente compatíveis com as strings e os números nativos do JavaScript. As enums são adequadas para cenários que exigem mapeamento reverso (valor → nome) ou nos quais é necessário iterar sobre todos os membros em tempo de execução. A Lição 12 fará uma comparação entre os dois.

P: O typeof consegue detectar todos os tipos? R: Não. O typeof só consegue identificar os seguintes tipos: “number”, “string”, “booleano”, “symbol”, “bigint”, “objeto”, “função” e “indefinido”. Para tipos de referência, como null (typeof null retorna “objeto”), matrizes e Date, é necessário usar instanceof ou outros métodos para restringir o tipo. Consulte a Lição 17 para obter uma explicação detalhada.

P: Quando o tipo never é usado? R: O tipo never aparece principalmente em dois cenários: (1) ele surge automaticamente quando um tipo é restringido a um ramo impossível; e (2) quando uma função nunca retornará um valor (como quando uma exceção é lançada ou ocorre um laço infinito). Iniciantes precisam apenas entender o padrão de “verificação exaustiva” — usar never na cláusula default do switch para verificar se há algum case faltando.

📖 Resumo

📝 Exercícios

  1. Problema básico (Dificuldade ⭐): Defina um tipo de união Status = "active" | "inactive" | "banned" e escreva uma função getStatusText(status: Status) que retorne a descrição em chinês correspondente.
  2. Problema avançado (Dificuldade ⭐⭐): Defina um tipo de união distinguível Result = SuccessResult | ErrorResult, em que SuccessResult é composto por success: true e data: string, e ErrorResult é composto por success: false e error: string. Escreva uma função que processe Result e, com base no valor de success, safely accesses either data or erro.
  3. Problema de desafio (Dificuldade ⭐⭐⭐): Escreva uma função describeDay(day: "mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun") usando verificação exaustiva para lidar com todos os 7 dias em uma instrução switch. Em seguida, omita intencionalmente um dia e observe a mensagem de erro gerada pela verificação exaustiva “never”.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%