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
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:
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:
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
}
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
let direction: "north" | "south" | "east" | "west";
direction = "north"; // ✅
direction = "up"; // ❌ "up" Not in a composite type
(3) Tipos de literais numéricos
type HttpStatusCode = 200 | 301 | 404 | 500;
let code: HttpStatusCode = 200; // ✅
let code2: HttpStatusCode = 201; // ❌ 201 Not in a composite type
(4) Tipos literais booleanos
type YesNo = true | false; // equivalent to boolean
type StrictTrue = true; // It can only be true
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
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
Saída:
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
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
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
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
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
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));
Saída:
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:
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
}
}
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
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
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:
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;
}
}
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
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";
}
}
Saída:
// 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
typeofconsegue detectar todos os tipos? R: Não. Otypeofsó consegue identificar os seguintes tipos: “number”, “string”, “booleano”, “symbol”, “bigint”, “objeto”, “função” e “indefinido”. Para tipos de referência, comonull(typeof nullretorna “objeto”), matrizes eDate, é necessário usarinstanceofou 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 tiponeveraparece 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” — usarneverna cláusuladefaultdoswitchpara verificar se há algumcasefaltando.
📖 Resumo
- Um tipo de união utiliza
|para combinar vários tipos, indicando que o valor pode ser qualquer um deles - Os tipos literais restringem os valores a literais específicos (cadeias de caracteres, números, valores booleanos) e, quando combinados com tipos de união, funcionam como enumerações.
- Variáveis de tipos de união só podem acessar propriedades compartilhadas por todos os membros; elas devem ser “restritas” usando
typeof,inouinstanceofpara acessar membros específicos. - Uma união discriminada é um padrão clássico que combina uma união e um literal — distinguindo tipos com base nos valores de atributos compartilhados.
- O tipo
neveré utilizado para uma verificação exaustiva, a fim de garantir que uma instruçãoswitchabranja todas as possibilidades.
📝 Exercícios
- Problema básico (Dificuldade ⭐): Defina um tipo de união
Status = "active" | "inactive" | "banned"e escreva uma funçãogetStatusText(status: Status)que retorne a descrição em chinês correspondente. - Problema avançado (Dificuldade ⭐⭐): Defina um tipo de união distinguível
Result = SuccessResult | ErrorResult, em queSuccessResulté composto porsuccess: trueedata: string, eErrorResulté composto porsuccess: falseeerror: string. Escreva uma função que processeResulte, com base no valor desuccess, safely accesses eitherdataorerro. - 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”.