惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 聂微东
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
月光博客
月光博客
博客园 - 三生石上(FineUI控件)
The Cloudflare Blog
博客园 - Franky
IT之家
IT之家
V
Visual Studio Blog
博客园 - 【当耐特】
阮一峰的网络日志
阮一峰的网络日志
V
V2EX
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 司徒正美
爱范儿
爱范儿
Hugging Face - Blog
Hugging Face - Blog
宝玉的分享
宝玉的分享
博客园 - 叶小钗
有赞技术团队
有赞技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
酷 壳 – CoolShell
酷 壳 – CoolShell
量子位
罗磊的独立博客
小众软件
小众软件
Jina AI
Jina AI

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Typescript: Refinamento de Tipos (Type Guards / Narrowing)
Yuri Peixinho · 2026-06-14 · via DEV Community

Yuri Peixinho

Introdução

  • Type guards → provam o tipo explicitamente
  • Narrowing → é o processo mais amplo, que inclui type guards e outras formas de afunilar
Narrowing
├── Type Guards
   ├── typeof
   ├── instanceof
   ├── in

├── Equality Narrowing
   ├── === / !==
│   └── == / != (loose, menos comum)
│
└── Truthiness Narrowing
    ├── if (x)
    ├── x && ...
    └── !!x

Type Guards

Um type guard é qualquer expressão que, quando avaliada como true, garante ao TypeScript qual é o tipo dentro daquele bloco.

O problema que eles resolvem

type Resultado = { dados: string[] } | { mensagem: string };

function tratar(r: Resultado) {
  console.log(r.dados); // ❌ erro — TypeScript não sabe qual dos dois é
}

Você precisa provar ao compilador qual tipo está lidando. Type guards fazem isso.

Typeof

O typeof é um operador do próprio JavaScript, o TypeScript apenas o observa e usa o resultado para afunilar o tipo.

O typeof funciona para estruturas primitivas como: string, number, boolean, undefined, symbol, bigint

O que acontece no exemplo passo a passo

function formatar(valor: string | number) {
  // aqui: valor é  string | number

  if (typeof valor === "string") {
    // aqui: TypeScript SABE que é string
    // métodos de string disponíveis ✅
    return valor.toUpperCase();
  }

  // aqui: TypeScript ELIMINOU string da union
  // só sobrou number ✅
  return valor.toFixed(2);
}

Esse "sobrar" é o narrowing acontecendo. O TypeScript rastreia o fluxo e descarta o que foi provado impossível. Por isso o nome traduzido é “estreitamento”, os tipos das variáveis vão se estreitando a medida que o typescript vai elimitando os tipos.

O typeof não distingue objetos entre si:

type NFe  = { chaveAcesso: string };
type NFSe = { codigoVerificacao: string };

function processar(nota: NFe | NFSe) {
  if (typeof nota === "object") {
    // TS ainda não sabe se é NFe ou NFSe ❌
    // typeof não ajuda aqui
  }
}

Para objetos você precisa do in, instanceof, ou um user-defined guard — que são os outros type guards que veremos em seguida.

Instanceof

O instanceof verifica se um objeto foi criado por uma determinada classe — tecnicamente, se o prototype da classe aparece na cadeia de prototypes do objeto.

Sintaxe básica:

objeto instanceof Classe

Retorna true ou false, e o TypeScript usa isso para afunilar o tipo.

Exemplo simples

class ErroDeRede extends Error {
  statusCode: number;
  constructor(msg: string, code: number) {
    super(msg);
    this.statusCode = code;
  }
}

class ErroDeValidacao extends Error {
  campo: string;
  constructor(msg: string, campo: string) {
    super(msg);
    this.campo = campo;
  }
}

function tratar(err: Error) {
  if (err instanceof ErroDeRede) {
    console.log(err.statusCode); // ✅ TypeScript sabe que é ErroDeRede
  }

  if (err instanceof ErroDeValidacao) {
    console.log(err.campo); // ✅ TypeScript sabe que é ErroDeValidacao
  }
}

Passo a passo do Narrowing

function tratar(err: ErroDeRede | ErroDeValidacao) {
  // aqui: err é ErroDeRede | ErroDeValidacao

  if (err instanceof ErroDeRede) {
    // aqui: err é ErroDeRede ✅
    return;
  }

  // aqui: TypeScript eliminou ErroDeRede
  // só sobrou ErroDeValidacao ✅
  console.log(err.campo);
}

Só funciona com classes — não funciona com type ou interface:

type NFe = { chaveAcesso: string };

const nota = { chaveAcesso: "123" };

nota instanceof NFe // ❌ erro de compilação — NFe não é uma classe

Para objetos simples tipados com type ou interface, você usa in ou um user-defined guard com is.

IN

O in verifica se uma propriedade existe em um objeto. É o tipo guard ideal para distinguir entre tipos que são objetos literais (type ou interface), onde instanceof não funciona.

Sintaxe básica

"propriedade" in objeto

Retorna true se a propriedade existe no objeto ou em seu prototype.

Exemplo simples

type Sucesso = { dados: string[] };
type Erro    = { mensagem: string };

function tratar(r: Sucesso | Erro) {
  if ("dados" in r) {
    console.log(r.dados); // ✅ TypeScript sabe que é Sucesso
  } else {
    console.log(r.mensagem); // ✅ TypeScript sabe que é Erro
  }
}

Passo a passo do narrowing

function tratar(r: Sucesso | Erro) {
  // aqui: r é Sucesso | Erro

  if ("dados" in r) {
    // aqui: r é Sucesso ✅
    return;
  }

  // aqui: TypeScript eliminou Sucesso
  // só sobrou Erro ✅
  console.log(r.mensagem);
}

Narrowing

Narrowing é o processo pelo qual o TypeScript reduz um tipo amplo para um mais específico conforme analisa o fluxo do código. Ele não é uma feature isolada — é um comportamento contínuo do compilador.

Alguns desenvolvedores confudem O Type Guard com o Narrow. Podemos dizer que o Type Guard é uma ferramenta que dispara o narrowing

function processar(valor: string | number) {
  // aqui: string | number

  if (typeof valor === "string") {
    // aqui: string
  }

  // aqui: number
}

O TypeScript rastreia o que é possível em cada ponto do código e descarta o que foi provado impossível.

Todas as formas de narrowing juntas

Narrowing
├── typeof           primitivos
├── instanceof       classes
├── in               objetos literais
├── User-defined     lógica customizada (is)
├── Equality         === / !== / == null
├── Truthiness       if (x) / !!x
└── Exhaustiveness   assertNever

, isCada uma é só uma ferramenta diferente para provar ao compilador qual tipo está presente.

O compilador acumula evidências

O narrowing é cumulativo, cada verificação elimina possibilidades:

function processar(valor: string | number | null | undefined) {
  if (valor == null) return;
  // eliminou null e undefined → string | number

  if (typeof valor === "string") return;
  // eliminou string → number

  // aqui: só number ✅
  console.log(valor.toFixed(2));
}

Equality

O TypeScript observa comparações de igualdade e usa o resultado para afunilar o tipo. Existem dois operadores: o estrito (=== / !==) e o loose (== / !=).

=== estrito

Compara valor e tipo — o mais comum:

function processar(x: string | number) {
  if (x === "pendente") {
    // ✅ x é string — e especificamente "pendente"
    return x.toUpperCase();
  }
  // aqui: number
}

!== para exclusão

function processar(valor: string | null) {
  if (valor !== null) {
    // ✅ null foi eliminado — só string
    console.log(valor.toUpperCase());
  }
}

== loose — o único caso útil

Normalmente evitamos == em TypeScript, mas há um caso onde ele é genuinamente melhor: eliminar null e undefined ao mesmo tempo.

function processar(valor: string | null | undefined) {
  // === exigiria duas verificações:
  if (valor !== null && valor !== undefined) { ... }

  // == faz os dois de uma vez:
  if (valor != null) {
    // ✅ null e undefined eliminados — só string
    console.log(valor.toUpperCase());
  }
}

Isso funciona porque null == undefined é true em JavaScript — os dois se cancelam mutuamente com ==.

Truthiness

Truthiness narrowing acontece quando o TypeScript observa se um valor é truthy ou falsy dentro de uma condição e afunila o tipo com base nisso.

Valores falsy em JavaScript

Esses são os únicos valores que avaliam como false num if:

false
0
-0
0n        // bigint zero
""        // string vazia
null
undefined
NaN

Tudo o mais é truthy — incluindo [], {}, "false", -1.

Exemplo básico

function processar(valor: string | null) {
  if (valor) {
    // ✅ null foi eliminado — só string
    console.log(valor.toUpperCase());
  }
}

Type Predicates — guards customizados

Quando nenhum guard nativo basta, você cria o seu com a sintaxe param is Tipo. O TypeScript confia na sua implementação e estreita o tipo em todo lugar que a função for chamada.

// Sintaxe: retorno "param is Tipo" em vez de boolean
function isString(v: unknown): v is string {
  return typeof v === "string";
}

// Caso real: validar shape de objeto externo
type NotaFiscal = { chave: string; valor: number; cnpj: string };

function isNotaFiscal(obj: unknown): obj is NotaFiscal {
  return (
    typeof obj === "object" && obj !== null &&
    "chave" in obj && typeof (obj as any).chave === "string" &&
    "valor" in obj && typeof (obj as any).valor === "number" &&
    "cnpj"  in obj && typeof (obj as any).cnpj  === "string"
  );
}

// Uso — TypeScript estreita o tipo automaticamente
function processarPayload(raw: unknown) {
  if (!isNotaFiscal(raw)) throw new Error("Payload inválido");
  // ✅ raw é NotaFiscal aqui — .chave, .valor, .cnpj disponíveis
  console.log(raw.chave, raw.valor);
}

// Filtrando arrays com predicate (remove undefined do tipo)
const ids: (string | undefined)[] = ["a", undefined, "b"];
const validos: string[] = ids.filter(
  (x): x is string => x !== undefined
); // ✅ tipo inferido como string[]

No fundo, tudo que vimos — typeof, instanceof, in, is, equality, truthiness — são apenas formas diferentes de fornecer evidências para o compilador afunilar o tipo com segurança.