Skip to content

Instantly share code, notes, and snippets.

@mearns
Last active November 5, 2020 13:49
Show Gist options
  • Save mearns/22969f35e9e1ec57daa14a2a20d7e026 to your computer and use it in GitHub Desktop.
Save mearns/22969f35e9e1ec57daa14a2a20d7e026 to your computer and use it in GitHub Desktop.
Error Utilities for JavaScript (NodeJS)
/*
* Create a new Error to wrap another Error.
*
* @params {Error} cause The error you want to wrap
* @params {string} [name] Optionally specify the `name` property for the error. You can also
* specify a `name` property in the `props` arg. If neither is given, the name of the given `cause` is
* used.
* @params {string} [message] Optionally specify the `message` property for the error. You can also
* specify a `message` property in the `props` arg. If neither is given, the `message` of the given `cause` is
* used.
* @params {object} [props] Optionally specify an object of properties to attach to the generated error.
*/
function wrapError(cause, name = null, message = null, props = {}) {
const newError = Object.assign(
new Error(message || props.message || cause.message),
cause,
props,
{
cause,
errors: [
cause
]
}
);
newError.name = name || props.name || cause.name;
if (typeof Error.captureStackTrace === "function") {
Error.captureStackTrace(newError, wrapError);
}
return newError;
}
/**
* This is useful if your logging tool doesn't have special handling of Error values, because the 3 key properties
* of an Error---name, message, and stack---are not enumerable by default, so if you're iterating over the enumerable
* properties, using `JSON.stringify` for instance, you won't actually get any of those properties.
*
* Note that it actually **mutates** the given object, it doesn't return a new object. Also note that if the given
* object has a magic-getter configured for the `name` property, that will be **replaced** with the next value returned
* by that getter.
*/
function makeErrorVisible(error) {
Object.defineProperty(error, "name", { ...Object.getOwnPropertyDescriptor(error, "name"), enumerable: true, value: error.name });
Object.defineProperty(error, "message", { ...Object.getOwnPropertyDescriptor(error, "message"), enumerable: true });
Object.defineProperty(error, "stack", { ...Object.getOwnPropertyDescriptor(error, "stack"), enumerable: true });
return error;
}
@blicksky
Copy link

blicksky commented Nov 3, 2020

I'm not sure if this serves the same purpose, but AggregateError is at least related: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError. It was added in V8 8.5, which is included in Node 15: https://nodejs.medium.com/node-js-v15-0-0-is-here-deb00750f278.

@mearns
Copy link
Author

mearns commented Nov 5, 2020

Awesome, thanks @blicksky! That's definitely relevant. I can't really decide how much overlap there is: definitely a good amount, and it might be effectively a replacement for wrapError. It makes me think that at least wrapError could set newError.errors = [cause], either instead of or in addition to the .cause property, to match the interface.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment