Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add isErrorLike() method #68

Merged
merged 12 commits into from
Apr 4, 2022
Merged
41 changes: 41 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ export type ErrorObject = {
code?: string;
} & JsonObject;

export type ErrorLike = {
name: string;
stack: string;
message: string;
code?: string;
} & JsonObject;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

& JsonObject is not correct here because the JS code is not actually ensuring that.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also add cause as optional? Same with ErrorObject.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

& JsonObject is not correct here because the JS code is not actually ensuring that.

I suppose the solution is to use a generic Record then

Should we also add cause as optional? Same with ErrorObject.

Done


export interface Options {
/**
The maximum depth of properties to preserve when serializing/deserializing.
Expand Down Expand Up @@ -120,3 +127,37 @@ console.log(error);
```
*/
export function deserializeError(errorObject: ErrorObject | unknown, options?: Options): Error;

/**
Predicate to determine whether a value looks like an error, even if it's not an instance of `Error`. It must have at least the `name`, `message`, and `stack` properties.

@example
```js
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
```js

Copy link
Collaborator Author

@fregante fregante Apr 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rest of the examples use ``` too. I dropped js though.

If you want to drop ``` from all of them it can be done in a separate commit I suppose

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to drop ``` from all of them it can be done in a separate commit I suppose

👍

import {isErrorLike} from 'serialize-error';

const error = new Error('🦄');
error.one = {two: {three: {}}};

isErrorLike({
name: 'DOMException',
message: 'It happened',
stack: 'at foo (index.js:2:9)',
});
//=> true

isErrorLike(new Error('🦄'));
//=> true

isErrorLike(serializeError(new Error('🦄'));
//=> true

isErrorLike({
name: 'Bluberricious pancakes',
stack: 12,
ingredients: 'Blueberry',
});
//=> false
```

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

*/
export function isErrorLike(value: unknown): value is ErrorLike;
10 changes: 9 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export function serializeError(value, options = {}) {
// People sometimes throw things besides Error objects…
if (typeof value === 'function') {
// `JSON.stringify()` discards functions. We do too, unless a function is thrown directly.
return `[Function: ${(value.name || 'anonymous')}]`;
return `[Function: ${value.name || 'anonymous'}]`;
}

return value;
Expand All @@ -161,3 +161,11 @@ export function deserializeError(value, options = {}) {

return new NonError(value);
}

export function isErrorLike(value) {
return value
&& typeof value === 'object'
&& 'name' in value
&& 'message' in value
&& 'stack' in value;
}
33 changes: 32 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const error = new Error('🦄');
console.log(error);
//=> [Error: 🦄]

const serialized = serializeError(error)
const serialized = serializeError(error);

console.log(serialized);
//=> {name: 'Error', message: '🦄', stack: 'Error: 🦄\n at Object.<anonymous> …'}
Expand Down Expand Up @@ -122,3 +122,34 @@ console.log(serializeError(error, {maxDepth: 1}));
console.log(serializeError(error, {maxDepth: 2}));
//=> {name: 'Error', message: '…', one: { two: {}}}
```

### isErrorLike(value)

Predicate to determine whether a value looks like an error, even if it's not an instance of `Error`. It must have at least the `name`, `message`, and `stack` properties.

```js
import {isErrorLike} from 'serialize-error';

const error = new Error('🦄');
error.one = {two: {three: {}}};

isErrorLike({
name: 'DOMException',
message: 'It happened',
stack: 'at foo (index.js:2:9)',
});
//=> true

isErrorLike(new Error('🦄'));
//=> true

isErrorLike(serializeError(new Error('🦄'));
//=> true

isErrorLike({
name: 'Bluberricious pancakes',
stack: 12,
ingredients: 'Blueberry',
});
//=> false
```
25 changes: 24 additions & 1 deletion test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Buffer} from 'node:buffer';
import Stream from 'node:stream';
import test from 'ava';
import {serializeError, deserializeError} from './index.js';
import {serializeError, deserializeError, isErrorLike} from './index.js';

function deserializeNonError(t, value) {
const deserialized = deserializeError(value);
Expand Down Expand Up @@ -383,3 +383,26 @@ test('should serialize properties up to `Options.maxDepth` levels deep', t => {
const levelThree = serializeError(error, {maxDepth: 3});
t.deepEqual(levelThree, {message, name, stack, one: {two: {three: {}}}});
});

test('should identify serialized errors', t => {
t.true(isErrorLike(serializeError(new Error('I’m missing more than just your body'))));
// eslint-disable-next-line unicorn/error-message -- Testing this eventuality
t.true(isErrorLike(serializeError(new Error())));
t.true(isErrorLike({
name: 'Error',
message: 'Is it too late now to say sorry',
stack: 'at <anonymous>:3:14',
}));

t.false(isErrorLike({
name: 'Bluberricious pancakes',
stack: 12,
ingredients: 'Blueberry',
}));

t.false(isErrorLike({
name: 'Edwin Monton',
message: 'We’ve been trying to reach you about your car’s extended warranty',
medium: 'Glass bottle in ocean',
}));
});