CodeBlanks Logo Icon CodeBlanks

Exhaustive eval

Write an evaluation function with exhaustive check.

Exhaustive eval

The evaluation function is written with a switch covering all node types. With the assertNever helper, the default branch gives a compile error if a case is missing. This ensures all eval functions are updated when a new node is added.

function assertNever(x: never): never {
  throw new Error("Unexpected: " + x);
}

function evalNode(n: Node): number | string {
  switch (n.kind) {
    case "number": return n.value;
    case "string": return n.value;
    case "add": return "(add)";
    default: return assertNever(n);
  }
}

Safety with assertNever

With assertNever, the default branch guarantees the switch covers all cases. When a new node type is added, the evalNode function is no longer exhaustive; n in the default branch is not never and the assertNever(n) call gives a compile error.

function assertNever(x: never): never {
  throw new Error("Unexpected");
}

function evalNode(n: Node): string {
  switch (n.kind) {
    case "number": return String(n.value);
    case "string": return n.value;
    case "add": return evalNode(n.left) + evalNode(n.right);
    default: return assertNever(n);
  }
}

Practice Write an exhaustive eval

Define an assertNever(x: never): never function (with throw new Error("Unexpected") inside). Define the Node discriminated union type ("number" (value: number), "string" (value: string), "add" (left: Node, right: Node)). Write an evalNode(n: Node): string function using switch (n.kind): for number return String(n.value), for string return n.value, for add return evalNode(n.left) + evalNode(n.right), for default write return assertNever(n);. Print the result via console.log(evalNode({ kind: "add", left: { kind: "number", value: 1 }, right: { kind: "string", value: "x" } })).

Starter code

index.ts
// assertNever fonksiyonunu tanımla (never parametre, throw ile)

// Node discriminated union türünü tanımla (kind: number, string, add)

// evalNode fonksiyonunu switch ile yaz, default'ta assertNever çağır

// evalNode ile add düğümünü değerlendir ve yazdır

Sample solution

index.ts
function assertNever(x: never): never {
  throw new Error("Unexpected");
}

type Node =
  | { kind: "number"; value: number }
  | { kind: "string"; value: string }
  | { kind: "add"; left: Node; right: Node };

function evalNode(n: Node): string {
  switch (n.kind) {
    case "number":
      return String(n.value);
    case "string":
      return n.value;
    case "add":
      return evalNode(n.left) + evalNode(n.right);
    default:
      return assertNever(n);
  }
}

console.log(evalNode({ kind: "add", left: { kind: "number", value: 1 }, right: { kind: "string", value: "x" } }));

Expected output

1x

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 What does assertNever provide in an exhaustive eval function?

  • A runtime error
  • When a new node is added, the compiler reports the missing case Correct answer
  • Automatic case insertion
  • Faster execution

Explanation: With assertNever the default branch guarantees the switch covers all cases; when a new node is added, missing cases cause compile errors.

2 assertNever in default branch

Question
function assertNever(x: never): never {
  throw new Error("Unexpected");
}

function evalNode(n: Node): string {
  switch (n.kind) {
    case "number": return String(n.value);
    case "string": return n.value;
    case "add": return evalNode(n.left) + evalNode(n.right);
    default:
      return ____;
  }
}
Answer
function assertNever(x: never): never {
  throw new Error("Unexpected");
}

function evalNode(n: Node): string {
  switch (n.kind) {
    case "number": return String(n.value);
    case "string": return n.value;
    case "add": return evalNode(n.left) + evalNode(n.right);
    default:
      return assertNever(n);
  }
}

Explanation: Calling assertNever(n) in the default branch gives a compile error if n is not never (i.e. a case is missing); this provides exhaustive check.

3 Exhaustive eval parts

Term Meaning
assertNever(n) errors if n is not never
default branch remaining after all cases
Yeni düğüm eklenince missing case errors at compile
switch (n.kind) covers all node types

All lessons in Advanced