What can you throw in JavaScript?
Any value at all. The language does not check what you throw, so a string, a number, an object or an Error instance are all valid, and the catch block receives exactly what you threw.
// Anything can be thrown.
try {
throw "a plain string";
} catch (err) {
console.log(typeof err, err); // "string" "a plain string"
}
try {
throw { code: 404, message: "Not found" };
} catch (err) {
console.log(err.code); // 404
}
// Error objects carry a stack.
try {
throw new TypeError("bad type");
} catch (err) {
console.log(err instanceof Error); // true
console.log(err.name); // "TypeError"
console.log(err.stack.split("\n")[0]);
}The freedom is real, and it is also a trap. Because a thrown value might not be an Error, any code that catches an error has to check before it reads the message property. Code that assumes err.message always exists will produce a confusing failure when something throws a string, and that happens more often than you would like in older code bases.
There is a good reason to prefer Error objects even though they are not required. They carry a name that tells you the category of the failure, a stack that tells you where it came from, and a cause that lets you chain errors together. A thrown string carries none of that, so all the useful context has to be formatted into the string by hand, which makes the result harder to log and impossible to inspect programmatically.
One more thing worth knowing. Throwing inside a catch block is a perfectly normal thing to do, and it is how you add context to a low level failure. But be careful about throwing from a finally block, because that will replace whatever the try block was in the middle of doing.