Return after several days. Without opening the worked answers, rebuild a key-dependent getter, a generic Result mapper and a string-array decoder. Explain where keyof, T[K] and a type parameter preserve information that any would discard.
Then add a new action to an exhaustive reducer and use the compiler to find the missing handler. Restore a complete switch. Write one negative type test and one runtime test, explaining which defect each detects.
Finally, rebuild a stale-completion scenario with controlled promises. Give both the old and new result valid types, and prove the older one still cannot replace the newer one. This is the boundary between static relationships and runtime time ordering.
If you needed the solution for the stock brief, retry it next week with a different domain such as ticket counts. Avoid solving the task by assertions; the goal is truthful contracts and tested behavior, not merely making diagnostics disappear.
Predict, run, and explain
Write the output and trace the important state changes before checking it. Then run the program. Use Check types first; execution is allowed only when the real compiler accepts the source.
function get<T, K extends keyof T>(value: T, key: K): T[K] {
return value[key];
}
console.log(get({ title: "Review", pages: 0 }, "pages"));Enable JavaScript to edit and type-check this example.
Check your prediction
0 Your turn
Come Back Later: Review Challenges: practise
Write a generic mapResult<A,B> for a result union with ok/value or error/message. Map a numeric success to text and preserve a failure unchanged. Print 0 items and missing.
// Narrow before calling the callback, and do not turn a failure into success.Enable JavaScript to edit and type-check this example.
Hint 1: the idea
The two type parameters describe a relationship between accepted and produced values.
Hint 2: a first step
Only invoke transform in the success branch.
Show one solution
type Result<T> =
| {
ok: true;
value: T;
}
| {
ok: false;
message: string;
};
function mapResult<A, B>(
result: Result<A>,
transform: (value: A) => B
): Result<B> {
if (!result.ok) {
return result;
}
return { ok: true, value: transform(result.value) };
}
const a = mapResult<number, string>(
{ ok: true, value: 0 },
value => `${value} items`
);
const b = mapResult<number, string>(
{ ok: false, message: "missing" },
value => `${value} items`
);
console.log(a.ok ? a.value : a.message);
console.log(b.ok ? b.value : b.message);Enable JavaScript to edit and type-check this example.
0 items
missingExplain why the implementation meets the stated contract. Which part is enforced by the language or checker, and which part needs behavioral evidence?
Change it and test it
Prove the transform is not called on failure with a call counter. Recreate the public projection exercise and demonstrate that a smaller type alone does not remove a secret runtime field. For further work, choose a real API problem before introducing conditional or recursive type machinery.
Completion is saved in this browser only. It does not measure mastery.