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.

avatar

A practical blog about TypeScript, React, and modern front-end development. Real-world tips, code breakdowns, and honest takes for developers who want to build better web apps—without the hype.

Subscribe to the newsletter