Typing an API response you do not control

Back to list2025-02-11
You

How do I type JSON from an external API safely?

ChatGPT

Parse, do not cast. A cast is a promise you cannot keep:

const User = z.object({ id: z.number(), email: z.string().email() });
type User = z.infer<typeof User>;
const user = User.parse(await res.json());

The runtime check is the point: as User compiles happily and fails at three in the morning instead.

You

Is unknown really better than any?

ChatGPT

Yes, because it forces a check before use. any disables the checker silently and spreads through everything it touches; unknown keeps the value opaque until you narrow it, which is exactly the discipline you wanted when you reached for a type.

You

How do I model loading and error states without booleans?

ChatGPT

A discriminated union removes impossible states:

type State =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "ok"; data: User[] }
  | { status: "error"; message: string };

With three booleans you can represent loading and error simultaneously; with this you cannot, and the compiler enforces handling each case.