Come Back Later: Review Challenges

The Course
•
3 min read

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.

Predict, then runTypeScript
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

Exercise

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.

Your workspaceTypeScript
// 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
One possible solutionTypeScript
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
missing

Explain 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.

Was this article helpful?

Your response is saved on this device.