The hole: types don't check what you got
In lesson 16 we wrote types, and the compiler started protecting us. But there's a hole — and it's important to understand it, so you don't trust types blindly.
response.json() returns any. Think about what that means:
const data = await response.json(); // data: any
const pics: Apod[] = data; // ✅ the compiler is happy... but WHY?
any turns off type checking. The compiler believes data is Apod[] because you said so. But it never actually checked. If NASA changes the response shape tomorrow (renames url to image_url), the types say nothing — the app breaks at runtime while TypeScript stays silent.
This is the key idea: types describe what you EXPECT; they don't check what you GOT. TypeScript won't save you from an API that changed, from corrupt JSON, or from a null where you expected an object. Types are a compile-time promise, not a runtime shield.
The fix — validate at the BOUNDARY. At the point where untrusted data enters the app (a network response, localStorage, the URL), we write a type guard:
function isApod(value: unknown): value is Apod { /* checks the fields */ }
The return type value is Apod is special. It tells the compiler: "if this function returned true, you may treat value as an Apod." That's how untrusted unknown becomes a trusted type — but only after a real, runtime check.
And two related ideas we'll add:
Readonly<AppState>— views can read state but not change it. The only way to change it is an action (like lesson 9, now enforced).catch (error)isunknown, notany— because JavaScript allowsthrow 42.unknownforces you to narrow before using it.