@typescript-eslint/only-throw-error

Disallow throwing values that are not Error objects

needs types

Also called typescript-eslint/only-throw-error, typescript/only-throw-error

You can throw any value, but only an Error records the stack where it was created. Code that catches it also expects an Error. With throw "not found", error.message is undefined and error instanceof Error is false.

By default, you can throw values of type any or unknown, and you can rethrow an error that you caught.

bun lint --type-aware turns this rule on in TypeScript files.

Configure#

Off by default. This rule needs types, so turning it on makes bun lint type check your code first.

bunfig.toml
[lint.rules]
"@typescript-eslint/only-throw-error" = "error" # or "warn"

Every option, set to its default:

bunfig.toml
[lint.rules]
"@typescript-eslint/only-throw-error" = ["error", {
  allowThrowingAny = true,
  allowThrowingUnknown = true,
  allowRethrowing = true,
  allow = [], # list of strings or objects
}]

Examples#

example.ts3 problems
function load(path: string, status: number) {
  if (!path) throw "path is required";
  if (status === 404) throw { status };
  throw undefined;
}
example.tsNo problems
class HttpError extends Error {
  constructor(public status: number) {
    super(`Request failed with ${status}`);
  }
}

function load(path: string, status: number) {
  if (!path) throw new Error("path is required");
  throw new HttpError(status);
}

Options#

OptionDefaultWhat it does
allowThrowingAnytrueAllow throwing a value of type any.
allowThrowingUnknowntrueAllow throwing a value of type unknown.
allowRethrowingtrueAllow throwing the error from catch (error), from .catch(error => ...), and from the second callback of .then(), whatever its type.
allow[]Other types you can throw.
example.tswith { "allowThrowingAny": false }1 problem
function fail(reason: any) {
  throw reason;
}

In strict mode, the error in a catch is unknown. With allowThrowingUnknown: false, you can still throw it again:

example.tswith { "allowThrowingUnknown": false }No problems
function parse(text: string): unknown {
  try {
    return JSON.parse(text);
  } catch (error) {
    console.error("invalid JSON");
    throw error;
  }
}
example.tswith { "allowThrowingUnknown": false, "allowRethrowing": false }1 problem
function parse(text: string): unknown {
  try {
    return JSON.parse(text);
  } catch (error) {
    console.error("invalid JSON");
    throw error;
  }
}

Each allow entry is a type name, or an object that also says where the type comes from: { "from": "file", "name": "Redirect" } for your own code, { "from": "lib", "name": "Response" } for a built-in type, or { "from": "package", "name": "Redirect", "package": "my-router" }.

example.tswith { "allow": [{ "from": "file", "name": "Redirect" }] }No problems
class Redirect {
  constructor(public url: string) {}
}

throw new Redirect("/login");