Careeroza
Typing fetch Responses
response.json() is Not Typed
fetch returns Promise<Response>, and response.json() returns Promise<any> (or unknown, depending on the type definitions). TypeScript cannot know what the server sends. You decide the type, ideally after validating it.
type Product = { id: number; name: string; price: number };
async function getProducts(): Promise<Product[]> {
const res = await fetch("https://api.shopease.in/products");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return (await res.json()) as Product[]; // trust the API (not checked at runtime)
}A Typed fetch Helper (Simulated)
Example
The examples cannot use the network, so a fake fetch builds real Response objects. The getJson helper checks the HTTP status and runs a validator, so callers get a correctly typed value or an error.
type Product = { id: number; name: string; price: number };
async function fakeFetch(url: string): Promise<Response> {
if (url.endsWith("/products/1")) {
return new Response(JSON.stringify({ id: 1, name: "Kettle", price: 1299 }), { status: 200 });
}
return new Response(JSON.stringify({ message: "Not found" }), { status: 404 });
}
async function getJson<T>(url: string, isT: (x: unknown) => x is T): Promise<T> {
const res = await fakeFetch(url);
if (!res.ok) throw new Error(`GET ${url} failed with ${res.status}`);
const data: unknown = await res.json();
if (!isT(data)) throw new Error("Unexpected response shape");
return data;
}
const isProduct = (x: unknown): x is Product =>
typeof x === "object" && x !== null && "id" in x && "name" in x && "price" in x;
async function main() {
const p = await getJson("/api/products/1", isProduct);
console.log(p.name, p.price);
try {
await getJson("/api/products/99", isProduct);
} catch (e) {
console.log((e as Error).message);
}
}
main();Output
Kettle 1299 GET /api/products/99 failed with 404