Course outline · 0% complete

0/26 lessons0%

Course overview →

Typing API Data

lesson 7-3 · ~13 min · 23/26

The boundary problem

Everything so far checks code you wrote. But data crossing into your program — a web API response sent by another server (lesson 2-3), a file, user input — is typed by nobody. JSON.parse returns any by design, and lesson 2-3 warned you where any leads.

The professional pattern has three steps:

  1. Declare an interface for the shape you expect
  2. Hold the parsed value as unknown, not any
  3. Write a checking function that proves the shape at runtime, then use the data fully typed

Step 3 uses a type predicate, a return type of the form value is ApiUser. It tells the compiler: when this function returns true, treat the argument as that type from here on. It is narrowing (Unit 4) that you define yourself.

JSON textunknown shapeisApiUser(data)runtime checkApiUserfully typedfalse → reject the payload
Data enters as unknown, passes a runtime check, and only then flows onward with a real type.

as: the assertion inside the checker

One new keyword appears in the checking function below: value as { id?: unknown }. The as keyword is a type assertion — it tells the compiler "treat this value as this type", with no runtime check and no conversion. The compiler simply believes you. That makes as dangerous as a general tool: assert the wrong type and you have re-created the any problem with extra steps, a claim the running program can still violate.

The legitimate, narrow use is exactly what the checker does. After proving value is a non-null object, it asserts a shape whose properties are all typed unknown — so nothing becomes trusted, and each property must still pass its own typeof test before use. The assertion claims no more than "an object whose properties I am about to check". That is the rule for as in general: use it only when you know something the compiler cannot, and keep the claim as weak as possible.

Code exercise · typescript

Run it. Then break the payload, change "id": 1 to "id": "1" in the raw string, and run again to see the check reject it at runtime.

Quiz

Why type the JSON.parse result as unknown instead of ApiUser directly?

Code exercise · typescript

Your turn. Declare interface Product { name: string; price: number }. Parse raw = '{"name": "Keyboard", "price": 49}' into data: unknown. Write isProduct(value: unknown): value is Product following the lesson's pattern, then print name and price if the check passes, or "bad payload" otherwise.