Skip to content

Commit

Permalink
Add isErrorLike() method (#68)
Browse files Browse the repository at this point in the history
  • Loading branch information
fregante committed Apr 4, 2022
1 parent adc780d commit bb6d9d6
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 3 deletions.
44 changes: 44 additions & 0 deletions index.d.ts
Expand Up @@ -5,8 +5,18 @@ export type ErrorObject = {
stack?: string;
message?: string;
code?: string;
cause?: unknown;
} & JsonObject;

export type ErrorLike = {
[key: string]: unknown;
name: string;
stack: string;
message: string;
code?: string;
cause?: unknown;
};

export interface Options {
/**
The maximum depth of properties to preserve when serializing/deserializing.
Expand Down Expand Up @@ -115,3 +125,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
```
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
```
*/
export function isErrorLike(value: unknown): value is ErrorLike;
10 changes: 9 additions & 1 deletion index.js
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
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 @@ -117,3 +117,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
@@ -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',
}));
});

0 comments on commit bb6d9d6

Please sign in to comment.