//
Busca en todas las páginas de la documentación
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Tipifica tus respuestas de API de extremo a extremo, desde llamadas fetch hasta la renderización de componentes. Usa guardas de tipo y validación en tiempo de ejecución para cerrar la brecha entre datos de red sin tipificar y tus tipos de TypeScript.
// Define tus tipos de API
type ApiUser = {
id: number;
name: string;
email: string;
role: "admin" | "editor" | "viewer";
};
type ApiResponse<T> = {
data: T;
meta: {
page: number;
totalPages: number;
totalCount: number;
};
};
// Wrapper fetch type-safe
async function fetchJson<T>(url: string): Promise<T> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json() as Promise<T>;
}
// Uso en un componente
function UserList() {
const [result, setResult] = useState<ApiResponse<ApiUser[]> | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
fetchJson<ApiResponse<ApiUser[]>>("/api/users")
.then(setResult)
.catch((err) => setError(err.message));
}, []);
if (error) return <p>Error: {error}</p>;
if (!result) return <p>Loading...</p>;
return (
<ul>
{result.data.map((user) => (
<li key={user.id}>{user.name} ({user.role})</li>
))}
</ul>
);
}fetch devuelve Response, y response.json() devuelve Promise<any>. La conversión as Promise<T> le dice a TypeScript qué forma esperar, pero no valida los datos en tiempo de ejecución.fetchJson<T>) centraliza el manejo de errores y la tipificación. Cada sitio de llamada especifica la forma de respuesta esperada.Validación de Zod en tiempo de ejecución:
import { z } from "zod";
const ApiUserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
role: z.enum(["admin", "editor", "viewer"]),
});
type ApiUser = z.infer<typeof ApiUserSchema>;
const ApiResponseSchema = <T extends z.ZodType>(dataSchema: T) =>
z.object({
data: dataSchema,
meta: z.object({
page: z.number(),
totalPages: z.number(),
totalCount: z.number(),
}),
});
async function fetchValidated<T>(url: string, schema: z.ZodType<T>): Promise<T> {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const json = await response.json();
return schema.parse(json); // Lanza ZodError si la validación falla
}
// Uso
const result = await fetchValidated(
"/api/users",
ApiResponseSchema(z.array(ApiUserSchema))
);Guarda de tipo personalizada:
function isApiUser(value: unknown): value is ApiUser {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
"name" in value &&
"email" in value &&
"role" in value &&
typeof (value as ApiUser).id === "number" &&
typeof (value as ApiUser).name === "string"
);
}
// Uso
const data: unknown = await response.json();
if (isApiUser(data)) {
console.log(data.name); // TypeScript sabe que data es ApiUser
}Tipificación de respuesta de error:
type ApiError = {
message: string;
code: string;
details?: Record<string, string[]>;
};
type ApiResult<T> =
| { success: true; data: T }
| { success: false; error: ApiError };
async function fetchApi<T>(url: string): Promise<ApiResult<T>> {
try {
const response = await fetch(url);
const json = await response.json();
if (!response.ok) {
return { success: false, error: json as ApiError };
}
return { success: true, data: json as T };
} catch {
return { success: false, error: { message: "Network error", code: "NETWORK" } };
}
}response.json() devuelve Promise<any>. La conversión as Promise<T> es un compromiso necesario ya que el análisis de JSON es inherentemente sin tipificar.z.infer<typeof Schema> deriva el tipo de TypeScript de un esquema de Zod, dándote una única fuente de verdad.unknown en lugar de any para datos sin validar. Te obliga a reducir o validar antes de acceder a propiedades.as T proporciona cero seguridad en tiempo de ejecución. Si la API cambia su formato de respuesta, tu código fallará silenciosamente hasta que un acceso a propiedad falle.fetch no lanza errores en respuestas 4xx o 5xx. Debes verificar response.ok o response.status manualmente.Date). Transfórmalos en una capa de mapeo..data en null, causando errores en tiempo de ejecución.| Enfoque | Pros | Contras |
|---|---|---|
Conversión as T | Simple, cero dependencias | Sin validación en tiempo de ejecución |
| Validación de Zod | Seguridad en tiempo de compilación y ejecución, única fuente de verdad | Dependencia agregada, costo de análisis |
Guardas de tipo (funciones is) | Sin dependencias, reducción explícita | Tedioso para tipos complejos, fácil de errar |
| tRPC | Seguridad de tipo de extremo a extremo, sin tipificación manual | Requiere configuración de servidor y cliente |
| GraphQL codegen | Tipos generados a partir del esquema | Paso de compilación, ecosistema de GraphQL requerido |
as Promise<T> para convertir, o valida con schema.parse(json) de Zod para seguridad en tiempo de ejecución.response.ok, capturar errores de red).const UserSchema = z.object({
id: z.number(),
name: z.string(),
});
type User = z.infer<typeof UserSchema>; // tipo derivado
const data = UserSchema.parse(json); // validación en tiempo de ejecuciónz.infer<typeof Schema> deriva el tipo de TypeScript del esquema.schema.parse(json) lanza un ZodError si los datos no coinciden en tiempo de ejecución.as T es una aseveración solo en tiempo de compilación con cero seguridad en tiempo de ejecución.fetch solo rechaza en fallos de red (errores de DNS, sin conexión).response.ok o response.status manualmente.Date).type ApiResult<T> =
| { success: true; data: T }
| { success: false; error: ApiError };success actúa como el discriminante.data y error en función de la comprobación de success..data en un estado null causa un error en tiempo de ejecución.unknown te obliga a reducir o validar antes de acceder a propiedades.any permite silenciosamente todo acceso a propiedades sin comprobaciones.const data: unknown = await response.json() como punto de partida para validación.Revisado por Chris St. John·Última actualización: 10 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥