CodeBlanks Logo Icon CodeBlanks

Type guards

Add a type guard for each node.

Type guard for a node

Writing a type guard for each node type allows external code to safely narrow the node. A guard of the form isNumberNode(n: Node): n is NumberNode narrows n to NumberNode when it returns true.

type NumberNode = { kind: "number"; value: number };

function isNumberNode(n: Node): n is NumberNode {
  return n.kind === "number";
}

Multiple guards

Writing guards for multiple node types makes complex branching clean. The isNumberNode(n), isStringNode(n), isAddNode(n) guards can also narrow with if chains instead of switch.

function isStringNode(n: Node): n is StringNode {
  return n.kind === "string";
}

function isAddNode(n: Node): n is AddNode {
  return n.kind === "add";
}

function process(n: Node): string {
  if (isNumberNode(n)) return "num:" + n.value;
  if (isStringNode(n)) return "str:" + n.value;
  return "add";
}

Practice Write node type guards

Define the Node discriminated union type ("number" (value: number), "string" (value: string), "add" (left: Node, right: Node)). Define NumberNode and StringNode types. Write isNumberNode(n: Node): n is NumberNode and isStringNode(n: Node): n is StringNode guard functions (each checking with n.kind === "..."). Write a describe(n: Node): string function: if isNumberNode(n) is true, return "num:" + n.value; else if isStringNode(n) is true, return "str:" + n.value; else return "add". Print the result via console.log(describe({ kind: "string", value: "x" })).

Starter code

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

// NumberNode ve StringNode türlerini tanımla

// isNumberNode ve isStringNode guard fonksiyonlarını yaz

// describe fonksiyonunu guard'larla yaz

// describe({ kind: "string", value: "x" }) sonucunu yazdır

Sample solution

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

type NumberNode = { kind: "number"; value: number };
type StringNode = { kind: "string"; value: string };

function isNumberNode(n: Node): n is NumberNode {
  return n.kind === "number";
}

function isStringNode(n: Node): n is StringNode {
  return n.kind === "string";
}

function describe(n: Node): string {
  if (isNumberNode(n)) return "num:" + n.value;
  if (isStringNode(n)) return "str:" + n.value;
  return "add";
}

console.log(describe({ kind: "string", value: "x" }));

Expected output

str:x

Questions and answers

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

1 What is the benefit of writing a type guard for an AST node?

  • It makes the code longer
  • It increases runtime speed
  • It reduces the number of nodes
  • It allows external code to safely narrow the node Correct answer

Explanation: A type guard allows external code to narrow a node to the correct type in an if condition; clean branching can also be done with an if chain instead of switch.

2 isNumberNode predicate

Question
function isNumberNode(n: Node): ____ {
  return n.kind === "number";
}
Answer
function isNumberNode(n: Node): n is NumberNode {
  return n.kind === "number";
}

Explanation: The n is NumberNode predicate tells TypeScript that n is a NumberNode when the function returns true.

3 Type guards parts

Term Meaning
isNumberNode(n) narrows n to NumberNode
n is NumberNode predicate return type
n.kind === "number" runtime check
if (isNumberNode(n)) narrowing with if chain

All lessons in Advanced