How to create / throw / catch the built-in Error object?
try {
    throw new Error("error message");
}
catch (e) {
    // handle the Error...
}
Which properties does the built-in Error object have?
try {
    throw new Error("error message");
}
catch (e) {
    console.log(`name: ${e.name}, message: ${e.message}`);
    // output:
    // name: Error, message: error message
}
What is the output of logging an Error object?
console.log(new Error("error message"));

/* the output contains name, message, and stack trace:
Error: error message
    at Object.<anonymous>
    ...
    ...
*/
What happens when trying to throw a string instead of an Error object?
try {
    throw "throw me";
}
catch (e) {
    console.log(typeof e);
    // output: string
    console.log(e);
    // output: throw me
}
How to extend the built-in Error with an error code?
class ErrorWithCode extends Error {
    constructor(message, code) {
        super(message);
        this.code = code;
    }
}

try {
    throw new ErrorWithCode("some message", "E-42");
} catch (e) {
    console.log(e);
    // output:
    // ErrorWithCode: some message
    //     at Object.<anonymous>
    //     ...
    //   code: 'E-42'
}