Challenge 3: Phantom-Type a Units System — Possible Solution ==================================================================== interface Distance { readonly value: number; readonly __unit?: Unit; // phantom — optional and never actually set at runtime } function meters(n: number): Distance<"meters"> { return { value: n }; } function feet(n: number): Distance<"feet"> { return { value: n }; } function addDistance( a: Distance, b: Distance ): Distance { return { value: a.value + b.value }; } // --- Usage --- const trackLength = addDistance(meters(100), meters(50)); console.log(trackLength.value); // 150 — both were "meters", so this is valid const fence = addDistance(feet(10), feet(6)); console.log(fence.value); // 16 — both were "feet", also valid // const invalid = addDistance(meters(100), feet(10)); // ❌ Distance<"feet"> is not assignable to Distance<"meters"> — addDistance's // generic Unit parameter is inferred from the FIRST argument (meters), // so the second argument (feet) fails to match it. // A conversion function is the intentional, explicit way to cross units: function feetToMeters(d: Distance<"feet">): Distance<"meters"> { return { value: d.value * 0.3048 }; } const mixed = addDistance(meters(100), feetToMeters(feet(10))); console.log(mixed.value); // 103.048 — now valid, because feetToMeters() // explicitly converted feet into meters first. WHY THIS WORKS -------------- - `__unit?: Unit` never actually holds a value at runtime (it's optional and no constructor ever sets it) — it exists purely so `Unit` appears somewhere in Distance's structure, which is what lets TypeScript treat Distance<"meters"> and Distance<"feet"> as different types even though both are `{ value: number }` at runtime. - addDistance(a: Distance, b: Distance) constrains both parameters to the SAME Unit — TypeScript infers Unit from the first argument, then requires the second argument to match that exact same Unit, which is what rejects mixing meters and feet directly. - feetToMeters() is the deliberate, explicit "escape hatch" for crossing units — exactly like Chapter 5's smart constructors were the only legitimate way to produce a branded value, unit conversion here is the only legitimate way to change a Distance's phantom Unit parameter.