Validando respostas JSON do fetch com Type Guards genéricos em TypeScript
Por John Pires · 19 de março de 2026

Validando respostas JSON do fetch com Type Guards genéricos em TypeScript
1) O que é e por que importa
Definição prática
Quando você usa fetch() no navegador ou fetch no Node para buscar dados, o que chega ao seu código é um objeto JavaScript — normalmente tipado como any ou unknown em TypeScript. Validar essa resposta significa verificar que o JSON tem a forma esperada antes de tratá-lo como um tipo forte no resto da aplicação.
Por que isso importa
Sem validação você assume que a API sempre retorna o que prometeu. Isso é frágil: mudanças no backend, erros de serialização, ou dados de terceiros quebram código em runtime. Type Guards genéricos permitem centralizar checagens e reaproveitar validação em tempo de execução, mantendo a ergonomia dos tipos TypeScript.
Para iniciantes: pensar em Type Guards como um filtro que transforma um valor de unknown em um tipo seguro, com código explícito que garante a forma do objeto. É a ponte entre o mundo dinâmico do JSON e o sistema de tipos estático do TypeScript.
2) Conceitos fundamentais com analogias
TypeScript x runtime
TypeScript verifica tipos em tempo de compilação — isso não existe no JavaScript que roda no navegador. Uma analogia: o TypeScript é como um manual de montagem; a validação runtime é checar se a peça real tem os furos no lugar antes de apertar os parafusos.
O que é um Type Guard
Um Type Guard é uma função que recebe um valor (geralmente unknown) e retorna um predicado do TypeScript do tipo v is T. Essa assinatura informa ao compilador que, após a chamada, o valor pode ser tratado como T.
Exemplo mental: imagine um detector que aponta "OK" quando um objeto atende o formato de um cartão de crédito. Enquanto o detector não disser "OK", você nunca processa o objeto como cartão de crédito.
3) Passo a passo com codigo comentado
Assinatura de um Type Guard genérico
Primeiro, a definição de um tipo para guards. Isso torna funções reutilizáveis e permite escrever helpers genéricos que aceitam qualquer guard.
Contexto: definimos o tipo de função guard que será usado por nossos validadores e pela função genérica de validação.
// Type guard type: recebe unknown e afirma que é T
export type TypeGuard<T> = (v: unknown) => v is T;
O que acontece: agora podemos anotar funções que declaram "este valor é do tipo T" de forma reutilizável.
Validador genérico simples
Contexto: função que aceita um valor e um guard, e lança erro se o valor não passa na checagem. Útil para centralizar tratamento de erro.
export function validateJson<T>(value: unknown, guard: TypeGuard<T>, name = 'payload'): T {
if (guard(value)) return value;
throw new Error(`Invalid ${name} received from server`);
}
O que acontece: ao chamar validateJson<User>(json, isUser), você obtém um User seguro ou um erro controlado.
Guard factory para objetos com propriedades simples
Contexto: muitos objetos são apenas um mapa de propriedades primárias (string, number, boolean). Uma fábrica de guard simplifica criar checadores para esses casos.
type PropCheck = (v: unknown) => boolean;
export function createObjectGuard<T extends Record<string, unknown>>(schema: Record<keyof T, PropCheck>): TypeGuard<T> {
return (v: unknown): v is T => {
if (typeof v !== 'object' || v === null) return false;
const obj = v as Record<string, unknown>;
for (const key in schema) {
if (!schema[key](obj[key])) return false;
}
return true;
};
}
O que acontece: cria-se um guard verificando cada chave do schema. Rápido para formas planas e legíveis para iniciantes.
4) Exemplo prático completo
Exemplo: consumindo uma API de usuários
Contexto: vamos modelar um tipo User, criar guard, fazer fetch e validar com nosso wrapper. Esse exemplo é mínimo, funcional e adequado para começar.
// types.ts
export type User = {
id: number;
name: string;
email: string;
isActive: boolean;
};
// checks.ts
const isNumber = (v: unknown) => typeof v === 'number' && !Number.isNaN(v);
const isString = (v: unknown) => typeof v === 'string';
const isBoolean = (v: unknown) => typeof v === 'boolean';
export const isUser = createObjectGuard<User>({
id: isNumber,
name: isString,
email: isString,
isActive: isBoolean,
});
O que acontece: definimos User e um guard isUser que valida cada propriedade. Isso evita acessar campos inexistentes mais adiante.
Wrapper de fetch com validação
Contexto: função reutilizável que busca JSON e aplica validação com nossa API genérica.
export async function fetchJson<T>(url: string, guard: TypeGuard<T>): Promise<T> {
const res = await fetch(url, { headers: { 'Accept': 'application/json' } });
if (!res.ok) throw new Error(`HTTP error ${res.status}`);
const json = await res.json();
return validateJson(json, guard, url);
}
// uso
(async () => {
try {
const user = await fetchJson<User>('https://api.exemplo.com/user/1', isUser);
console.log('Nome do usuário:', user.name);
} catch (err) {
console.error('Falha ao obter usuário:', err);
}
})();
O que acontece: a chamada garante que, se a resposta não tiver a forma esperada, o erro é lançado antes de qualquer uso inseguro dos campos.
Decorator (opcional) para validar retorno de método
Contexto: quem usa decorators pode aplicar validação automático no retorno de funções assíncronas — útil em serviços.
// decorator.ts
export function validateReturn<T>(guard: TypeGuard<T>) {
return function (target: any, _key: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = async function (...args: any[]) {
const result = await original.apply(this, args);
if (!guard(result)) throw new Error('Invalid return value from decorated method');
return result;
};
return descriptor;
};
}
// uso em classe
class UserService {
@validateReturn(isUser)
async getUser() {
const resp = await fetch('https://api.exemplo.com/user/1');
return resp.json(); // será validado pelo decorator
}
}
O que acontece: o decorator intercepta o retorno e garante a forma antes de propagar para quem chamou o método.
5) Erros comuns, armadilhas e dicas práticas
Erro 1: confiar em any
Usar any esconde problemas. Quando uma API muda, erros surgem em runtime sem pistas no código. Prefira sempre unknown e validação.
Erro 2: tentar fazer guards totalmente genéricos demais
Guards que tentam cobrir todos os casos (arrays aninhados, unions complexas) ficam gigantes e difíceis de manter. Comece com verificações planas e crie helpers para arrays e opcionais.
Lista de dicas práticas:
- Use
unknowncomo ponto de entrada e converta para tipos específicos com guards. - Para arrays, crie um helper
isArrayOfque recebe um guard de item. - Evite reflexões complexas: mantenha a validação previsível e testável.
Tip: escreva testes unitários para seus guards. Validadores são código crítico — bugs nele quebram toda a app.
Exemplo de helper para arrays e opcionais
Contexto: funções utilitárias simples para evitar repetir lógica quando validar listas ou campos opcionais.
export const isArrayOf = <T>(itemGuard: TypeGuard<T>): PropCheck => (v: unknown) => {
if (!Array.isArray(v)) return false;
return v.every(item => itemGuard(item));
};
export const isOptional = (check: PropCheck): PropCheck => (v: unknown) => v === undefined || check(v);
O que acontece: isArrayOf permite validar arrays de itens e isOptional lida com propriedades que podem faltar.
6) Próximos passos para aprofundar
Ferramentas de schema validation
Para projetos maiores, avalie bibliotecas como zod, io-ts ou ajv. Elas trazem geração de schemas, validação, mapeamento de erros e integração com TypeScript. Porém, entender Type Guards antes facilita a migração.
Testes, performance e ergonomia
Guards podem adicionar overhead. Meça quando validar grandes volumes. Em muitos casos valide na entrada (API boundary) e confie internamente. Escreva testes unitários para cada guard e cenários positivos/negativos.
Analise também como integrar validação em pipelines: middleware, services e handlers. Coloque validação na borda do sistema para reduzir checagens repetidas.
Conclusão prática
Type Guards genéricos são uma técnica simples e poderosa para tornar consumo de APIs robusto em TypeScript. Comece definindo guards para suas formas de dados, crie um wrapper fetchJson<T> que usa validateJson, e expanda com helpers (arrays, opcionais) e, se necessário, decorators para padronizar comportamento.
Pequenos investimentos em validação economizam horas de debugging. Aplique essas ideias em pontos de entrada do aplicativo e evolua para soluções de schema quando a complexidade aumentar.
Checklist rápido para aplicar hoje:
- Trocar
anyporunknownnas respostas de fetch. - Implementar
TypeGuard<T>evalidateJson. - Adicionar guards para os tipos mais usados e testar com unit tests.
- Medir impacto e considerar libs de schema quando necessário.
Com essas práticas você terá maior segurança, menos bugs silenciosos e um código TypeScript que reflete verdadeiramente a forma dos dados em runtime.
Assuntos: TypeScript, type-guards, fetch, validation
Leia também
Autorização type-safe em Node/Express com decorators, branded types e generics
Padrões práticos para autorização type-safe em Node/Express usando decorators, branded types e generics: evite strings mágicas, typagem de IDs e centralize…
Client HTTP type-safe com Fetch: decorators, generics e type guards
Padrão prático para construir um client HTTP type-safe com fetch usando decorators, generics e type guards: validação runtime, exemplos implementáveis e…
Config type-safe em Node.js com TypeScript: Zod, generics e utility types
Aprenda a fazer configuração type-safe em Node.js com TypeScript usando Zod, generics e utility types. Passo a passo prático para validar e inferir tipos do…
Feature flags type-safe em TypeScript: configuração, generics e type guards
Aprenda a implementar feature flags type-safe em TypeScript: defina keys literais, use generics, type guards e decorators para evitar typos e validar…
Converter JSON snake_case para camelCase com TypeScript e Generics
Aprenda passo a passo a converter JSON snake_case para camelCase usando TypeScript: funções runtime recursivas, tipos genéricos DeepCamelCase e boas práticas…
DTOs type-safe em TypeScript: decorators, generics e type guards passo a passo
Aprenda a transformar JSON em DTOs type-safe com TypeScript usando decorators, generics e type guards. Guia passo a passo para iniciantes: configuração…