Checkpoint: Parse Minutes

The Course
•
4 min read

Combine a numeric check with a result type. Your caller needs either a valid number of minutes or a readable error. A discriminated union can express that relationship without optional fields floating independently.

This task starts with a string, as a form input would. Use ordinary JavaScript conversion: Number(text). An empty or whitespace-only string converts to zero, so check blank text separately. Number.isFinite(value) returns true only for a finite numeric value; it rejects NaN and both infinities without converting strings. This is slightly different from JavaScript beginner’s integer check: our rule here allows zero and finite decimals, while that form exercise required whole minutes from 1 through 240. These are different product rules, not a change caused by TypeScript. Reject negative values too; the number annotation alone cannot do it.

Use { kind: "ok"; minutes: number } for success and { kind: "error"; message: string } for failure. A display function should inspect kind before reading the matching field. Do not force a result into one branch with an assertion.

Budget your time for edge cases, not just the first happy-path input. A parser that handles “25” but silently accepts a blank form has not met the brief. Type checking verifies that you return a valid variant; runtime checks decide which variant is truthful.

Predict, check, then run

Before running, write down the output and explain which information the checker knows. Click Check types first, then Check & run.

Predict, then runTypeScript
const blank = "   ";

console.log(Number(blank));
console.log(Number.isFinite(Number("oops")));
console.log(Number("25"));

Enable JavaScript to edit and type-check this example.

Check your prediction
0
false
25

Acceptance cases before you code

Write a small test plan before implementing expected result alternatives. These cases extend the brief; they are not a second worked solution. Predict the expected behavior independently, then test through the public function. A good ordinary example and a good boundary example should catch different possible mistakes.

Case to tryWhat your result should demonstrate
Numeric text zeroReturn the success variant
Blank or negative inputReturn the specified failure variant
Success branch renderingUse the discriminant so zero remains a valid result

After the core task passes, deliberately break one rule and identify the case that notices it. Restore the implementation and explain the failure in terms of the contract, not the number of assertions. If a case requires a concept you have not recalled, revisit the relevant lesson before opening the solution.

Your turn

Exercise

Checkpoint: Parse Minutes: practise

Implement parseMinutes(text: string) returning the Result union described above. Blank, non-finite and negative inputs return message “Invalid minutes”. Valid inputs, including zero, return ok. Print 25 min, 0 min and Invalid minutes for “25”, “0” and ” ”.

Your workspaceTypeScript
// Define Result, parseMinutes, and a display function. Test three inputs.

Enable JavaScript to edit and type-check this example.

Hint 1: the idea

Check the raw text for blankness before converting it.

Hint 2: a first step

Store Number(text) in a local variable, then check finiteness and the lower bound.

Show one solution
One possible solutionTypeScript
type Result =
  | {
      kind: "ok";

      minutes: number;
    }
  | {
      kind: "error";

      message: string;
    };

function parseMinutes(text: string): Result {
  const minutes = Number(text);

  if (text.trim() === "" || !Number.isFinite(minutes) || minutes < 0) {
    return { kind: "error", message: "Invalid minutes" };
  }

  return { kind: "ok", minutes };
}

function describe(result: Result): string {
  if (result.kind === "ok") {
    return `${result.minutes} min`;
  }

  return result.message;
}

console.log(describe(parseMinutes("25")));
console.log(describe(parseMinutes("0")));
console.log(describe(parseMinutes(" ")));

Enable JavaScript to edit and type-check this example.

25 min
0 min
Invalid minutes

Explain the types at the function boundary and the runtime checks, if any. Compare your approach before copying this answer.

Change it and explain it

Test “oops”, “Infinity”, “-1” and “2.5”. The stated contract permits finite decimals. If the product needs whole minutes, add Number.isInteger and update the contract. Explain what the compiler proves about Result and what your conditions prove about minutes.

Was this article helpful?

Your response is saved on this device.