TypeScript: Guards de tipo e restrição de tipo no TypeScript
Última atualização: 2026-08-26
O estreitamento de tipos é um dos recursos mais práticos do TypeScript — ele restringe tipos amplos a um escopo mais preciso, permitindo que você acesse com segurança propriedades e métodos de tipos específicos.
1. Visão geral do estreitamento de tipos
Variáveis de um tipo de união só podem acessar propriedades compartilhadas por todos os membros. O estreitamento de tipo nos permite “estreitar” o tipo em um ramo específico do código para acessar membros exclusivos desse ramo:
function process(value: string | number) {
// Before narrowing——value Only shared methods can be used
console.log(value.toString()); // ✅ string and number All of them
// After narrowing——value You can use a specific method
if (typeof value === "string") {
console.log(value.toUpperCase()); // ✅ string Unique Approach
} else {
console.log(value.toFixed(2)); // ✅ number Unique Approach
}
}
O TypeScript oferece suporte aos seguintes tipos de restrição:
| Método de restrição | Cenários aplicáveis |
|---|---|
typeof |
Verificação de tipo básica |
instanceof |
Verificação de instância de classe |
in Operador |
A propriedade existe? |
| Verificação de igualdade | Comparação ===/!== |
| Proteções de tipos personalizadas | Verificações de tipos complexos |
| Com opção de redução | Reduzir automaticamente durante a atribuição |
2. Restrição do typeof
typeof Mais comumente usado para distinguir entre tipos básicos:
(1) Uso básico
function padLeft(value: string, padding: string | number): string {
if (typeof padding === "number") {
return " ".repeat(padding) + value; // padding narrowed to number
}
return padding + value; // padding narrowed to string
}
console.log(padLeft("hello", 4)); // " hello"
console.log(padLeft("hello", ">>>")); // ">>>hello"
(2) O valor de retorno de typeof
typeof "hello" // "string"
typeof 42 // "number"
typeof true // "boolean"
typeof undefined // "undefined"
typeof Symbol() // "symbol"
typeof 100n // "bigint"
typeof {} // "object" ⚠️ Including null, Array, Date, etc.
typeof function(){} // "function"
typeof null === "object" é um bug histórico do JavaScript. Não é possível usar typeof para distinguir entre null e outros objetos — é preciso usar === null para verificar.
(3) typeof com switch
function describe(value: string | number | boolean | undefined) {
switch (typeof value) {
case "string":
return `String:${value.toUpperCase()}`;
case "number":
return `Numbers:${value.toFixed(2)}`;
case "boolean":
return `Boolean:${value}`;
case "undefined":
return "Undefined";
}
}
3. Restrição do instanceof
instanceof Verifica se um objeto é uma instância de uma determinada classe — adequado para restringir tipos de referência:
(1) Uso básico
function formatValue(value: Date | string | Error): string {
if (value instanceof Date) {
return value.toISOString(); // Date Methods
} else if (value instanceof Error) {
return value.message; // Error Properties
} else {
return value.toUpperCase(); // string Methods
}
}
console.log(formatValue(new Date())); // "2024-..."
console.log(formatValue(new Error("Error"))); // "Error"
console.log(formatValue("hello")); // "HELLO"
(2) Limitações do instanceof
instanceof Só pode ser usado com instâncias de classe — não com interfaces ou aliases de tipo (que não existem em tempo de execução):
interface Dog { bark(): void; }
interface Cat { meow(): void; }
// ❌ instanceof Cannot be used for interfaces
// if (pet instanceof Dog) { ... }
// ✅ Do you need to use a custom type guard or in Operator
(3) Classes personalizadas e instanceof
class NetworkError extends Error {
constructor(public statusCode: number) {
super(`Network Error:${statusCode}`);
}
}
class ValidationError extends Error {
constructor(public field: string) {
super(`Validation Error:${field}`);
}
}
function handleError(error: NetworkError | ValidationError): string {
if (error instanceof NetworkError) {
return `HTTP ${error.statusCode} Error`;
} else {
return `Field ${error.field} Invalid`;
}
}
4. Restrição do operador in
in Verifica se um objeto possui uma propriedade específica — útil para distinguir entre interfaces e aliases de tipo:
(1) Uso básico
interface Fish {
swim(): void;
}
interface Bird {
fly(): void;
}
function move(animal: Fish | Bird) {
if ("swim" in animal) {
animal.swim(); // ✅ Fish Type
} else {
animal.fly(); // ✅ Bird Type
}
}
(2) Atributos distinguíveis
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
function getArea(shape: Shape): number {
if (shape.kind === "circle") {
return Math.PI * shape.radius ** 2; // ✅ Circle 's radius
} else {
return shape.sideLength ** 2; // ✅ Square 's sideLength
}
}
▶ Exemplo: Tratamento de eventos com segurança de tipos
interface ClickEvent {
type: "click";
x: number;
y: number;
}
interface KeyEvent {
type: "keydown" | "keyup";
key: string;
ctrlKey: boolean;
}
interface ScrollEvent {
type: "scroll";
scrollTop: number;
scrollLeft: number;
}
type UIEvent = ClickEvent | KeyEvent | ScrollEvent;
function handleEvent(event: UIEvent): string {
switch (event.type) {
case "click":
return `Click here:(${event.x}, ${event.y})`;
case "keydown":
case "keyup":
return `Button:${event.key},Ctrl:${event.ctrlKey}`;
case "scroll":
return `Scroll:top=${event.scrollTop}, left=${event.scrollLeft}`;
}
}
console.log(handleEvent({ type: "click", x: 100, y: 200 }));
console.log(handleEvent({ type: "keydown", key: "Enter", ctrlKey: false }));
console.log(handleEvent({ type: "scroll", scrollTop: 50, scrollLeft: 0 }));
Saída:
Click here:(100, 200)
Button:Enter,Ctrl:false
Scroll:top=50, left=0
5. Guarda de tipos personalizados
Quando os métodos de restrição integrados não forem suficientes, você pode escrever funções personalizadas de verificação de tipo:
(1) Predicado de tipo
interface Dog {
bark(): void;
breed: string;
}
interface Cat {
meow(): void;
color: string;
}
// Type Predicates:The return value is "Parameter Name is Type"
function isDog(animal: Dog | Cat): animal is Dog {
return "bark" in animal;
}
function interact(animal: Dog | Cat) {
if (isDog(animal)) {
animal.bark(); // ✅ narrowed to Dog
console.log(animal.breed);
} else {
animal.meow(); // ✅ narrowed to Cat
console.log(animal.color);
}
}
(2) Função de afirmação
As funções de asserção lançam uma exceção quando a condição não é satisfeita — indicando ao TypeScript: “Se esta linha for executada, a condição deve ser verdadeira”:
function assertDefined<T>(value: T | undefined | null, message?: string): asserts value is NonNullable<T> {
if (value == null) {
throw new Error(message ?? "The value cannot be null or undefined");
}
}
function processUser(user: User | undefined) {
assertDefined(user, "User does not exist");
// After that user The type has been narrowed down to User(Excludes undefined)
console.log(user.name.toUpperCase()); // ✅ Safety
}
(3) Asserções versus predicados de tipo
| Propriedade | Predicado de tipo x is T |
Função de afirmação asserts x is T |
|---|---|---|
| Valor de retorno | booleano | nulo (lança uma exceção se a condição não for satisfeita) |
| Uso | if (isType(x)) |
assertIsType(x) |
| Restrição de tipo | Restrição em um ramo “if” | Restrição automática após uma chamada |
| Cenários adequados | Tratamento de ramificações pós-verificação | Verificações de pré-requisitos |
6. Restrição da atribuição
As operações de atribuição também realizam o estreitamento de tipos:
let value: string | number;
value = "hello";
console.log(value.toUpperCase()); // ✅ After assignment, it narrows to string
value = 42;
console.log(value.toFixed(2)); // ✅ After assignment, it narrows to number
(1) Análise do fluxo de controle
O TypeScript acompanha as mudanças no tipo de uma variável ao longo do fluxo de controle:
function example(x: string | number | boolean) {
// x: string | number | boolean
if (typeof x === "string") {
// x: string
console.log(x.toUpperCase());
} else {
// x: number | boolean
if (typeof x === "number") {
// x: number
console.log(x.toFixed(2));
} else {
// x: boolean
console.log(x);
}
}
}
(2) Restrição e reatribuição
let value: string | number;
value = "hello";
console.log(value.length); // ✅ string
value = 42;
// console.log(value.length); // ❌ number None length
value = true; // ❌ boolean Not in a composite type
7. Verificação exaustiva
Certifique-se de que a instrução switch/if abranja todos os tipos possíveis — use o tipo never para garantir a exaustividade:
type Shape = "circle" | "square" | "triangle";
function getIcon(shape: Shape): string {
switch (shape) {
case "circle": return "○";
case "square": return "□";
case "triangle": return "△";
default: {
// If all case It's all taken care of.,shape Here is never
const _exhaustive: never = shape;
return _exhaustive;
}
}
}
// If in the future Shape Added "hexagon" But I didn't add it case
// default Branched _exhaustive Report Type Error
// This is a reminder to fill in any missing information. case
(1) Uma verificação exaustiva mais concisa
function assertNever(value: never): never {
throw new Error(`Unprocessed values:${value}`);
}
type Action = "create" | "update" | "delete";
function handleAction(action: Action) {
switch (action) {
case "create": /* ... */ break;
case "update": /* ... */ break;
case "delete": /* ... */ break;
default:
assertNever(action); // If omitted case,A type error will be reported here.
}
}
▶ Exemplo: typeof e instanceof para estreitamento de tipos
function format(value: string | number | Date): string {
if (typeof value === "string") {
return value.trim().toUpperCase();
} else if (typeof value === "number") {
return value.toFixed(2);
} else if (value instanceof Date) {
return value.toISOString();
}
return String(value);
}
console.log(format(" hello ")); // "HELLO"
console.log(format(3.14159)); // "3.14"
console.log(format(new Date("2024-01-01"))); // "2024-01-01T00:00:00.000Z"
Saída:
HELLO
3.14
2024-01-01T00:00:00.000Z
▶ Exemplo: Guarda de tipo personalizado com uniões discriminadas
interface Circle { kind: "circle"; radius: number; }
interface Rectangle { kind: "rectangle"; width: number; height: number; }
type Shape = Circle | Rectangle;
function isCircle(shape: Shape): shape is Circle {
return shape.kind === "circle";
}
function area(shape: Shape): number {
if (isCircle(shape)) {
return Math.PI * shape.radius ** 2; // Circle
}
return shape.width * shape.height; // Rectangle
}
console.log(area({ kind: "circle", radius: 5 })); // 78.54
console.log(area({ kind: "rectangle", width: 4, height: 6 })); // 24
Saída:
78.53981633974483
24
❓ Perguntas Frequentes
P: Qual é a diferença entre
typeofeinstanceof? R:typeofverifica o “tipo primitivo” de um valor (string/número/booleano/indefinido/objeto/função) e retorna uma string; é adequado para verificar tipos primitivos.instanceofverifica se um valor é uma instância de uma determinada classe; é adequado para verificar tipos de referência (comoDate,Errore classes personalizadas). Os dois são complementares e não se excluem mutuamente.
P: Qual é o impacto no desempenho das verificações de tipo personalizadas? R: Nenhum. As verificações de tipo são usadas apenas em tempo de compilação — o JavaScript compilado consiste em instruções
ifcomuns e verificações de propriedades. Os predicados de tipo (x is T) e as funções de asserção (asserts x is T) não existem em tempo de execução, portanto, a sobrecarga é zero.
P: Por que o operador
inpermite o estreitamento de tipos? R: Porque o TypeScript sabe que, se um objeto possui uma determinada propriedade, ele deve pertencer a uma interface que inclua essa propriedade. Quando"swim" in animalé verdadeiro,animaldeve implementar uma interface que incluaswim. Trata-se de uma inferência lógica que não requer informações de tipo em tempo de execução.
P: Quando é necessário escrever verificadores de tipo personalizados? R: Quando os métodos integrados de restrição de tipo (typeof, instanceof, in, ===) não conseguem distinguir entre tipos. O cenário mais comum é a distinção entre interfaces — como as interfaces não existem em tempo de execução, não é possível verificá-las usando instanceof; em vez disso, é preciso usar in para verificar se há propriedades identificáveis ou escrever verificadores de tipo personalizados.
📖 Resumo
- O estreitamento de tipos — o processo de restringir um tipo amplo a um intervalo preciso — é fundamental para o uso seguro dos tipos de união.
typeofverifica se é um tipo primitivo,instanceofverifica se é uma instância de classe einverifica se uma propriedade existe- As proteções de tipo personalizadas (predicados de tipo
x is T) lidam com tipos complexos que não podem ser distinguidos por meio de métodos embutidos - A função de verificação (
asserts x is T) realiza uma verificação prévia e lança uma exceção caso a condição não seja atendida. - A análise de fluxo de controle do TypeScript rastreia automaticamente as alterações nos tipos das variáveis nas ramificações
- Utilize o tipo
neverpara verificações exaustivas, a fim de garantir que as instruçõesswitcheifabranjam todas as possibilidades
📝 Exercícios
- Problema básico (Dificuldade ⭐): Escreva uma função
doubleOrRepeat(value: string | number)— sevaluefor um número, multiplique-o por 2; se for uma sequência de caracteres, concatene-a consigo mesma uma vez (por exemplo, “hi” → “hihi”). Usetypeofpara restringir a entrada. - Problema avançado (Dificuldade ⭐⭐): Defina duas interfaces,
Admin(com o métodohasPermission) eGuest(com o métodorequestAccess), e refine-as usando o atributo distinguívelrole. Escreva uma função que chame métodos diferentes com base na função. - Desafio (Dificuldade: ⭐⭐⭐): Escreva um tipo de verificação personalizado
isNonNull<T>(value: T | null | undefined): value is NonNullable<T>e, em seguida, use-o na funçãofilterNonNull<T>(arr: (T | null | undefined)[]): T[]para filtrar valores nulos e indefinidos e retornar um array não vazio.