Lesson 15 of 30
A hand-written response type gives you autocomplete based on a type you wrote by hand — and nothing keeps that type honest as the real API drifts away from it.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 3
A client uses `return res.json() as Order` with a hand-written `Order` interface. The backend later renames a field the interface expects. What happens?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
A common first pattern for calling an API in TypeScript looks like this: write a fetch call, then assert the response as some hand-written interface with as SomeType. It compiles, it autocompletes, it looks type-safe. But the type only describes what the API returned when the developer wrote it down — nothing re-checks that assertion against what the API actually returns today. The API can add a field, rename one, or change a type, and the hand-written interface will happily keep compiling while quietly lying about the real shape.
Two real approaches close that gap:
as SomeType silently wrong.// Hand-written and asserted — nothing keeps this honest over time
type Order = { id: string; total: number };
async function getOrder(id: string): Promise<Order> {
const res = await fetch(`/api/orders/${id}`);
return res.json() as Order; // trusts the shape, doesn't check it
}
If the backend later renames total to totalCents and changes its type to an integer, this code keeps compiling. It just silently returns undefined for order.total everywhere, forever, until someone notices the bug in production.
// Schema-generated: the type comes from the API's own published spec
// (illustrative — the actual generator/tooling varies by project)
import type { paths } from "./generated/api-types"; // produced from the API's OpenAPI spec
type OrderResponse = paths["/orders/{id}"]["get"]["responses"]["200"]["content"]["application/json"];
async function getOrder(id: string): Promise<OrderResponse> {
const res = await fetch(`/api/orders/${id}`);
return res.json() as OrderResponse;
}
Regenerating generated/api-types after the backend's rename immediately turns every place order.total was read into a real compile error — the exact opposite of the hand-written version silently drifting.
as SomeType is a promise to the compiler, not a check. TypeScript trusts a type assertion unconditionally — it performs zero runtime verification that the actual value matches, which is exactly the gap codegen and end-to-end approaches close at compile time, and only real runtime validation closes at request time."How do you keep your frontend's types in sync with a backend that keeps changing?" is a real question that tests whether a candidate knows as SomeType is an unchecked promise, and can name concrete alternatives — schema-driven codegen, end-to-end type sharing, and runtime validation — along with which one actually fits an external API versus one your own team controls.