diff --git a/libraries/o-error/index.cjs b/libraries/o-error/index.cjs index ef08b45f16..0bb7da653a 100644 --- a/libraries/o-error/index.cjs +++ b/libraries/o-error/index.cjs @@ -1,20 +1,34 @@ +// @ts-check + /** * Light-weight helpers for handling JavaScript Errors in node.js and the * browser. */ class OError extends Error { + /** + * The error that is the underlying cause of this error + * + * @type {unknown} + */ + cause + + /** + * List of errors encountered as the callback chain is unwound + * + * @type {TaggedError[] | undefined} + */ + _oErrorTags + /** * @param {string} message as for built-in Error * @param {Object} [info] extra data to attach to the error - * @param {Error} [cause] the internal error that caused this error + * @param {unknown} [cause] the internal error that caused this error */ constructor(message, info, cause) { super(message) this.name = this.constructor.name if (info) this.info = info if (cause) this.cause = cause - /** @private @type {Array | undefined} */ - this._oErrorTags // eslint-disable-line } /** @@ -31,7 +45,7 @@ class OError extends Error { /** * Wrap the given error, which caused this error. * - * @param {Error} cause the internal error that caused this error + * @param {unknown} cause the internal error that caused this error * @return {this} */ withCause(cause) { @@ -65,13 +79,16 @@ class OError extends Error { * } * } * - * @param {Error} error the error to tag + * @template {unknown} E + * @param {E} error the error to tag * @param {string} [message] message with which to tag `error` * @param {Object} [info] extra data with wich to tag `error` - * @return {Error} the modified `error` argument + * @return {E} the modified `error` argument */ static tag(error, message, info) { - const oError = /** @type{OError} */ (error) + const oError = /** @type {{ _oErrorTags: TaggedError[] | undefined }} */ ( + error + ) if (!oError._oErrorTags) oError._oErrorTags = [] @@ -102,7 +119,7 @@ class OError extends Error { * * If an info property is repeated, the last one wins. * - * @param {Error | null | undefined} error any error (may or may not be an `OError`) + * @param {unknown} error any error (may or may not be an `OError`) * @return {Object} */ static getFullInfo(error) { @@ -129,7 +146,7 @@ class OError extends Error { * Return the `stack` property from `error`, including the `stack`s for any * tagged errors added with `OError.tag` and for any `cause`s. * - * @param {Error | null | undefined} error any error (may or may not be an `OError`) + * @param {unknown} error any error (may or may not be an `OError`) * @return {string} */ static getFullStack(error) { @@ -143,7 +160,7 @@ class OError extends Error { stack += `\n${oError._oErrorTags.map(tag => tag.stack).join('\n')}` } - const causeStack = oError.cause && OError.getFullStack(oError.cause) + const causeStack = OError.getFullStack(oError.cause) if (causeStack) { stack += '\ncaused by:\n' + indent(causeStack) } diff --git a/libraries/o-error/test/o-error-util.test.js b/libraries/o-error/test/o-error-util.test.js index c86cec44a2..b29dd4900a 100644 --- a/libraries/o-error/test/o-error-util.test.js +++ b/libraries/o-error/test/o-error-util.test.js @@ -268,6 +268,11 @@ describe('utils', function () { expect(OError.getFullInfo(null)).to.deep.equal({}) }) + it('works when given a string', function () { + const err = 'not an error instance' + expect(OError.getFullInfo(err)).to.deep.equal({}) + }) + it('works on a normal error', function () { const err = new Error('foo') expect(OError.getFullInfo(err)).to.deep.equal({}) diff --git a/libraries/o-error/test/o-error.test.js b/libraries/o-error/test/o-error.test.js index 84244ec92e..db87f56992 100644 --- a/libraries/o-error/test/o-error.test.js +++ b/libraries/o-error/test/o-error.test.js @@ -35,6 +35,14 @@ describe('OError', function () { expect(err2.cause.message).to.equal('cause 2') }) + it('accepts non-Error causes', function () { + const err1 = new OError('foo', {}, 'not-an-error') + expect(err1.cause).to.equal('not-an-error') + + const err2 = new OError('foo').withCause('not-an-error') + expect(err2.cause).to.equal('not-an-error') + }) + it('handles a custom error type with a cause', function () { function doSomethingBadInternally() { throw new Error('internal error')