- Published on
TypeScript Trick #3: How to Type fetch Response (Without Losing Your Mind)
Look, I love TypeScript. But damn, sometimes typing things like fetch()
responses feels like trying to teach a cat to sit. So let me show you how I deal with it — without overengineering or writing novels in types.
🚀 Step 1: Define the Type You Expect
Let’s say you’re fetching a user:
type User = {
id: number;
name: string;
email: string;
};
Keep it simple. No need to go full extends Record<string, unknown>
here.
🔁 Step 2: Create a Reusable Fetch Helper
async function fetchJson<T>(url: string, options?: RequestInit): Promise<T> {
const res = await fetch(url, options);
if (!res.ok) {
throw new Error(`Fetch failed with status: ${res.status}`);
}
const data = await res.json();
return data as T; // yeah yeah, this is "trust me bro" typing
}
Yes, I know - as T is technically lying to the compiler. But if you control the backend or trust your API, it’s a nice tradeoff between sanity and type safety.
🧪 Step 3: Use It Like a Boss
const getUser = async (id: number) => {
return await fetchJson<User>(`/api/users/${id}`);
};
const user = await getUser(1);
console.log(user.name); // typed, autocompleted, safe (ish)
Boom. Clean, readable, and TS gives you nice autocomplete. But here comes the spicy part...
Ok, but I want to make it safer!
If you don’t trust the API, use a schema validator like Zod on top:
import { z } from "zod";
const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
});
type User = z.infer<typeof UserSchema>;
async function fetchValidated<T>(url: string, schema: z.ZodSchema<T>): Promise<T> {
const res = await fetch(url);
const json = await res.json();
return schema.parse(json); // throws if the data doesn't match
}
const user = await fetchValidated("/api/users/1", UserSchema);
Now you’re not just telling TypeScript what shape you hope the data has - you’re making sure it does. Runtime meets static type safety. Like peanut butter and jelly.
Final Thoughts
- as T is fine for trusted APIs — just don’t overdo it
- Zod (or Yup or io-ts) = sanity for public / external APIs
- Wrap it in one helper, reuse everywhere, sleep better at night
That’s it. No decorators. No class-based fetch factories. Just a few lines of code that get the job done without TypeScript screaming at you.
Let me know if you wanna see how I use this in React components or with pagination/meta APIs.