Code
src/types.ts — the Apod discriminated union
In the code column — src/types.ts, the domain types. ApodImage and ApodVideo are two interfaces, and Apod is their union.
Three details do all the work:
media_type: 'image'(notstring) — a literal. That's the discriminant that enables narrowing.hdurl?: stringonApodImageonly, andApodVideodoesn't have it at all. The?means "optional" (string | undefined) — because even an image doesn't always have a high-res version.copyright?: stringon both — because any picture might have no owner (public domain).
The Apod union:
export type Apod = ApodImage | ApodVideo;
When you hold an Apod, the compiler doesn't know which one it is — so it won't let you reach hdurl until you check media_type. That's exactly what fixed lesson 15's TS2551: the check unlocks hdurl.
Rating — a literal union:
export type Rating = 0 | 1 | 2 | 3 | 4 | 5; // not number
Verify (in your head or with tsc).
const pic: Apod = /* ... */;
pic.hdurl; // ❌ TS2339/2551 — not every Apod has hdurl
if (pic.media_type === 'image') {
pic.hdurl; // ✅ now it exists
}
const r: Rating = 47; // ❌ Type '47' is not assignable to type 'Rating'
Tip. The
?(optional) and the|(union) are two different "might not be there".hdurl?: string= "this field may be absent".Apod = ApodImage | ApodVideo= "this object may be one of two shapes". Together they describe the messy reality exactly: sometimes a video, sometimes no copyright, sometimes no high-res.