Busca en todas las páginas de la documentación
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Tipa tus llamadas a useSWR con genéricos para obtener seguridad de tipos completa en data, error, claves y funciones fetcher. El soporte de TypeScript de SWR permite inferir tipos de retorno a partir de las firmas del fetcher.
"use client";
import useSWR from "swr";
interface User {
id: string;
name: string;
email: string;
}
const fetcher = (url: string): Promise<User> =>
fetch(url).then((r) => r.json());
function Profile() {
const { data, error } = useSWR<User, Error>("/api/me", fetcher);
// data: User | undefined
// error: Error | undefined
if (data) {
return <div>{data.name}</div>; // aquí data es User
}
}"use client";
import useSWR, { Fetcher, SWRConfiguration, Key } from "swr";
import useSWRMutation, { SWRMutationConfiguration } from "swr/mutation";
// ---- Factory de fetcher con tipos seguros ----
function createFetcher<T>(): Fetcher<T, string> {
return async (url: string) => {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json() as Promise<T>;
};
}
// ---- Tipos de dominio ----
interface Product {
id: string;
name: string;
price: number;
category: string;
}
interface CreateProductInput {
name: string;
price: number;
category: string;
}
// ---- Hooks con tipos seguros ----
function useProduct(id: string) {
return useSWR<Product, Error>(
`/api/products/${id}`,
createFetcher<Product>()
);
}
function useProducts(category?: string) {
const key = category ? `/api/products?category=${category}` : "/api/products";
return useSWR<Product[], Error>(key, createFetcher<Product[]>());
}
// ---- Mutación con tipos seguros ----
async function createProduct(
url: string,
{ arg }: { arg: CreateProductInput }
): Promise<Product> {
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(arg),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
}
function useCreateProduct() {
return useSWRMutation<Product, Error, string, CreateProductInput>(
"/api/products",
createProduct
);
}
// ---- Uso ----
export default function ProductPage({ id }: { id: string }) {
const { data: product, error, isLoading } = useProduct(id);
const { trigger, isMutating } = useCreateProduct();
if (isLoading) return <div>Cargando...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>{product?.name}</h1>
<p>${product?.price}</p>
<button
onClick={() =>
trigger({ name: "New Item", price: 29.99, category: "widgets" })
}
disabled={isMutating}
>
Añadir producto
</button>
</div>
);
}useSWR<Data, Error, Key> acepta hasta tres parámetros genéricos: el tipo de datos, el tipo de error y el tipo de clave.Data a partir del tipo de retorno del fetcher.Fetcher<Data, Key> de SWR exige que el fetcher acepte el tipo de clave y devuelva Promise<Data>.useSWRMutation<Data, Error, Key, Arg> añade un cuarto genérico para el tipo del argumento de mutación.SWRResponse, SWRConfiguration, Key y Fetcher para tipado avanzado.Tipos inferidos desde el fetcher:
// SWR infiere Data del tipo de retorno del fetcher
const fetchUser = (url: string): Promise<User> => fetch(url).then((r) => r.json());
const { data } = useSWR("/api/me", fetchUser);
// data: User | undefined - inferido desde fetchUserTipado de claves en array:
type FetcherArgs = [url: string, token: string];
const fetcher: Fetcher<User, FetcherArgs> = ([url, token]) =>
fetch(url, { headers: { Authorization: `Bearer ${token}` } }).then((r) => r.json());
const { data } = useSWR<User, Error, FetcherArgs>(
["/api/me", authToken],
fetcher
);Middleware tipado:
import { Middleware, SWRHook } from "swr";
const loggerMiddleware: Middleware = (useSWRNext: SWRHook) => {
return (key, fetcher, config) => {
const result = useSWRNext(key, fetcher, config);
console.log(`SWR [${key}]:`, result.data);
return result;
};
};Configuración de SWR tipada:
import type { SWRConfiguration } from "swr";
const defaultConfig: SWRConfiguration<User, Error> = {
revalidateOnFocus: false,
errorRetryCount: 3,
onSuccess: (data) => {
// data está tipado como User
console.log(data.name);
},
};as para seguridad de tipos. useSWR<User> es mejor que data as User.null), el tipo de datos sigue siendo Data | undefined incluso con suspense.Key acepta string, any[], null, undefined o () => Key.undefined de data aunque esperes que ya esté cargado.res.json() devuelve Promise<any> por defecto. Debes hacer un cast: res.json() as Promise<User>. Es un límite de confianza: no se realiza validación en tiempo de ejecución.useSWR, TypeScript puede ampliar silenciosamente a any. Comprueba siempre que tus tipos coincidan.error es any por defecto si no especificas el genérico Error. Especifícalo siempre para seguridad de tipos: useSWR<Data, Error>.useSWR en un hook personalizado, el tipo de retorno es SWRResponse<Data, Error>. Impórtalo para tipado explícito.Middleware en lugar de escribir la firma completa manualmente.| Enfoque | Ventajas | Desventajas |
|---|---|---|
| Genéricos explícitos en useSWR | Control total, tipos claros | Verboso, alineación manual |
| Inferidos desde el fetcher | Menos boilerplate | Fácil devolver any por accidente |
| Hooks personalizados tipados | Encapsulados, reutilizables | Capa de abstracción adicional |
| Validación en tiempo de ejecución (zod) | Seguridad real en runtime | Dependencia extra, coste de rendimiento |
const { data, error } = useSWR<User, Error>("/api/me", fetcher);
// data: User | undefined
// error: Error | undefinedHasta tres genéricos: useSWR<Data, Error, Key>.
Sí. Si tu fetcher tiene un retorno tipado, SWR infiere Data a partir de él:
const fetchUser = (url: string): Promise<User> =>
fetch(url).then((r) => r.json());
const { data } = useSWR("/api/me", fetchUser);
// data: User | undefined (inferido)res.json() siempre devuelve Promise<any> en TypeScript. Debes hacer un cast: res.json() as Promise<User>. Es un límite de confianza: TypeScript no puede validar la forma en tiempo de ejecución. Considera usar Zod para validación en runtime.
import { Fetcher } from "swr";
const fetcher: Fetcher<User[], string> = (url) =>
fetch(url).then((r) => r.json());Fetcher<Data, Key> exige que el fetcher acepte el tipo de clave y devuelva Promise<Data>.
useSWRMutation<Data, Error, Key, Arg>(key, mutationFn);Data: tipo de retorno de la mutaciónError: tipo de errorKey: tipo de la clave de cachéArg: tipo del argumento pasado a triggerTypeScript puede ampliar silenciosamente a any en lugar de mostrar un error de compilación. Comprueba siempre que el tipo de retorno de tu fetcher coincida con el genérico Data que pasas a useSWR.
type FetcherArgs = [url: string, token: string];
const fetcher: Fetcher<User, FetcherArgs> = ([url, token]) =>
fetch(url, {
headers: { Authorization: `Bearer ${token}` },
}).then((r) => r.json());
const { data } = useSWR<User, Error, FetcherArgs>(
["/api/me", authToken],
fetcher
);Al envolver useSWR en un hook personalizado, el tipo de retorno es SWRResponse<Data, Error>. Impórtalo desde swr para tipar explícitamente el valor de retorno de tu hook personalizado.
import { Middleware, SWRHook } from "swr";
const myMiddleware: Middleware = (useSWRNext: SWRHook) => {
return (key, fetcher, config) => {
return useSWRNext(key, fetcher, config);
};
};Usa la exportación del tipo Middleware en lugar de escribir la firma completa manualmente.
Prefiere siempre genéricos explícitos (useSWR<User>) frente a casts con as (data as User). Los genéricos proporcionan seguridad en tiempo de compilación en todo el uso del hook, mientras que los casts solo silencian el comprobador de tipos en un punto.
import type { SWRConfiguration } from "swr";
const config: SWRConfiguration<User, Error> = {
revalidateOnFocus: false,
onSuccess: (data) => {
console.log(data.name); // data está tipado como User
},
};Revisado por Chris St. John·Última actualización: 10 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥