diff --git a/spec/TinyType.spec.ts b/spec/TinyType.spec.ts index 5ee7fd62..6a496a27 100644 --- a/spec/TinyType.spec.ts +++ b/spec/TinyType.spec.ts @@ -280,6 +280,20 @@ describe('TinyType', () => { expect(parameters.toJSON()).to.deep.equal(['apples', 'bananas', 'cucumbers']); }); + it(`should serialise an Error as its stack trace`, () => { + class CustomError extends TinyTypeOf() { + } + + const error = thrown(new Error('example error')) + + const customError = new CustomError(error); + + expect(customError.toJSON()).to.deep.equal({ + message: 'example error', + stack: error.stack, + }); + }); + it('should serialise a plain-old JavaScript object with nested complex types recursively', () => { interface NotesType { authCredentials: { @@ -432,3 +446,11 @@ describe('TinyType', () => { }); }); }); + +function thrown(throwable: T): T { + try { + throw throwable; + } catch (error) { + return error as T; + } +} diff --git a/src/TinyType.ts b/src/TinyType.ts index 4ca9d69c..9adc52b2 100644 --- a/src/TinyType.ts +++ b/src/TinyType.ts @@ -150,6 +150,8 @@ function toJSON(value: any): JSONObject | NonNullJSONPrimitive { return toJSON(Array.from(value)); case value && isRecord(value): return recordToJSON(value); + case value && value instanceof Error: + return errorToJSON(value); case isSerialisablePrimitive(value): return value; default: @@ -170,6 +172,14 @@ function recordToJSON(value: Record): JSONObject { return Object.fromEntries(serialised); } +function errorToJSON(value: Error): JSONObject { + return Object.getOwnPropertyNames(value) + .reduce((serialised, key) => { + serialised[key] = toJSON(value[key]) + return serialised; + }, { }) as JSONObject; +} + function isSerialisableNumber(value: unknown): value is number { return typeof value === 'number' && ! Number.isNaN(value)