Skip to content

Errors ​

throw, try / catch / finally (with or without a catch binding), rethrow, and finally that runs on return, break and continue all behave as in Node. You can throw a string or one of the four built-in error classes. String(e) of a caught error prints Name: message, as in Node.

ts
// throw, try/catch/finally, rethrow, and an uncaught error's exit code, all as in Node.
function parsePort(s: string): number {
  const n = Number(s);
  if (!Number.isInteger(n) || n < 1 || n > 65535) {
    throw new Error(`bad port: ${s}`);
  }
  return n;
}

for (const input of ["8080", "http", "70000"]) {
  try {
    console.log("port", parsePort(input));
  } catch (e) {
    console.log(String(e));
  } finally {
    console.log("checked", input);
  }
}

function withCleanup(): string {
  try {
    return "result";
  } finally {
    console.log("cleanup runs before the return completes");
  }
}
console.log(withCleanup());
text
port 8080
checked 8080
Error: bad port: http
checked http
Error: bad port: 70000
checked 70000
cleanup runs before the return completes
result

An error that escapes the program ends it with exit status 1, as in Node. The test suite compares stdout and the exit code; the text of the report on stderr is not compared.

Error classes ​

Error, TypeError, RangeError and SyntaxError can be created with new, thrown, caught, and told apart with instanceof. After instanceof, read .message and .name:

ts
// The four built-in error classes: construct, throw, catch, test with instanceof, and read
// .message and .name. String(e) is "Name: message", as in Node.
function check(input: string): number {
  if (input === "") throw new SyntaxError("empty input");
  const n = Number(input);
  if (Number.isNaN(n)) throw new TypeError(`not a number: ${input}`);
  if (n < 0 || n > 100) throw new RangeError(`out of range: ${n}`);
  if (n === 13) throw new Error("unlucky");
  return n;
}

for (const input of ["42", "", "abc", "200", "13"]) {
  try {
    console.log("ok", check(input));
  } catch (e) {
    if (e instanceof RangeError) {
      console.log("range problem:", e.message);
    } else if (e instanceof TypeError || e instanceof SyntaxError) {
      console.log(e.name, "->", e.message);
    } else {
      console.log(String(e), e instanceof Error);
    }
  }
}

// A thrown string is caught as that string.
try {
  throw "plain text";
} catch (e) {
  console.log(String(e));
}
text
ok 42
SyntaxError -> empty input
TypeError -> not a number: abc
range problem: out of range: 200
Error: unlucky true
plain text

What a caught value is ​

A caught value is the value that was thrown. For a thrown string, typeof e is "string", e === "text" compares the text, a switch (e) matches its cases, an empty string is falsy, and Number(e) parses it. After typeof e === "string", e is that string. For an error, typeof e is "object" and === compares identity, as in Node:

ts
// A caught value is the value that was thrown: a thrown string stays a string.
function risky(n: number): void {
  if (n === 0) throw "zero is not allowed";
  if (n < 0) throw new RangeError(`negative: ${n}`);
}

for (const n of [0, -1, 1]) {
  try {
    risky(n);
    console.log(n, "ok");
  } catch (e) {
    if (typeof e === "string") {
      console.log(n, "string:", e.toUpperCase());
    } else if (e instanceof RangeError) {
      console.log(n, `${e.name}:`, e.message);
    }
    console.log(typeof e, e === "zero is not allowed", String(e));
  }
}
text
0 string: ZERO IS NOT ALLOWED
string true zero is not allowed
-1 RangeError: negative: -1
object false RangeError: negative: -1
1 ok

Limits today ​

  • A class cannot extend Error or any other built-in class (CS1000). Throw a built-in error, and keep extra data in a variable of its own or return a result object instead.
  • Only strings, the four error classes and caught values can be thrown. Throwing any other value (a number, an object, an instance of your own class, a value that may be undefined) is rejected (CS1247); throw new Error(String(x)) instead.
  • &&, || and ?? on a caught value are rejected (CS1248), because their result has no type ChadScript can hold. Test the value first, or convert it: String(e) || "default". So is reading e after a test that proves it is a number or an array, which a thrown value never is, and assigning to a caught value.
  • An error has no .stack, and its fields cannot be assigned (e.message = ...) (CS1000).
  • console.log(e) of a caught value is rejected (CS1238), because Node prints it with a stack trace. Print String(e) or e.message.

Next: JSON.