Skip to content

Commit

Permalink
feat: implement isCError & isWError methods
Browse files Browse the repository at this point in the history
These methods allows consumers to check the type of error recieved,
without resorting to `if(err instanceof CError)` which can prove quite
error-prone across different versions of the library.
  • Loading branch information
jdpnielsen committed Sep 22, 2020
1 parent ae87fb5 commit 62b52ea
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 2 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,13 @@ functions on the `CError` module. They're defined this way rather than using
methods on CError instances so that they can be used on Errors not created with
`CError`.

### `CError.isCError(err)`

The `isCError()` function returns a boolean, if the provided error is an instance
of CError or an instance of a class which inherited from CError. Under the hood,
the method uses a Symbol for checking, rather than `err instanceof CError`. This
allows compatability between versions of this library.

### `CError.cause(err)`

The `cause()` function returns the next Error in the cause chain for `err`, or
Expand Down
9 changes: 9 additions & 0 deletions src/lib/cerror.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ test('should JSON.stringify correctly', t => {
t.is(JSON.stringify(childError), '{"error":"CError","message":"ChildError"}');
});

test('should have a static .isCError method which returns true when given a CError', t => {
const regularError = new Error('regularError');
const cError = new CError('cError');

t.is(CError.isCError(null), false, 'handles null');
t.is(CError.isCError(regularError), false, 'handles Error');
t.is(CError.isCError(cError), true, 'handles CError');
});

test('should have a static .cause which returns the expected cause', t => {
const parentError = new Error('ParentError');
const childError = new CError('ChildError', parentError, { info: { foo: 'bar' } });
Expand Down
9 changes: 7 additions & 2 deletions src/lib/cerror.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ export interface Options {
constructorOpt?: Function;
}

export const CERROR_SYMBOL = Symbol.for('contextual-error/cerror');

export class CError extends Error {
public readonly name: string = 'CError';
public readonly message!: string;

public readonly info: Info = {};

/**
Expand All @@ -48,9 +49,9 @@ export class CError extends Error {
*/
private readonly cause?: Error | CError;


constructor(message?: string, cause?: Error, options?: Options) {
super(message);
Object.defineProperty(this, CERROR_SYMBOL, { value: true });
this.shortMessage = this.message;

if (options?.info) {
Expand Down Expand Up @@ -105,6 +106,10 @@ export class CError extends Error {
};
}

public static isCError(obj: unknown): boolean {
return (obj as {[CERROR_SYMBOL]?: boolean})?.[CERROR_SYMBOL] != null;
}

public static cause(err: CError | Error): CError | Error | null {
if ((err as CError).cause) {
return (err as CError).cause as CError | Error;
Expand Down
21 changes: 21 additions & 0 deletions src/lib/werror.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,27 @@ test('should JSON.stringify correctly', t => {
t.is(JSON.stringify(childError), '{"error":"WError","message":"ChildError"}');
});

test('should have a static .isCError method which returns true when given a CError', t => {
const regularError = new Error('RegularError');
const cError = new CError('CError');
const wError = new WError('WError');

t.is(WError.isCError(regularError), false, 'handles Error');
t.is(WError.isCError(cError), true, 'handles CError');
t.is(WError.isCError(wError), true, 'handles WError');
});

test('should have a static .isWError method which returns true when given a WError', t => {
const regularError = new Error('RegularError');
const cError = new CError('CError');
const wError = new WError('WError');

t.is(WError.isWError(null), false, 'handles null');
t.is(WError.isWError(regularError), false, 'handles Error');
t.is(WError.isWError(cError), false, 'handles CError');
t.is(WError.isWError(wError), true, 'handles WError');
});

test('should have a static .cause which returns the expected cause', t => {
const parentError = new Error('ParentError');
const childError = new WError('ChildError', parentError, { info: { foo: 'bar' } });
Expand Down
7 changes: 7 additions & 0 deletions src/lib/werror.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import CError, { Options } from './cerror';

export const WERROR_SYMBOL = Symbol.for('contextual-error/werror');

export class WError extends CError {
public readonly name: string = 'WError';

constructor(message?: string, cause?: Error, options?: Options) {
super(message, cause, Object.assign(options || {}, { skipCauseMessage: true }));
Object.defineProperty(this, WERROR_SYMBOL, { value: true });

if (options?.name) {
this.name = options.name;
}
}

public static isWError(obj: unknown): boolean {
return (obj as { [WERROR_SYMBOL]?: boolean })?.[WERROR_SYMBOL] != null;
}
}

export default WError;

0 comments on commit 62b52ea

Please sign in to comment.