From f710535fdb2460d5b8a55be745a3d9465008d22b Mon Sep 17 00:00:00 2001 From: leow93 Date: Wed, 11 Oct 2023 22:02:38 +0100 Subject: [PATCH 1/2] feat: enum support --- src/lib/json-decode.spec.ts | 55 +++++++++++++++++++++++++++++++++++-- src/lib/json-decode.ts | 8 ++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/lib/json-decode.spec.ts b/src/lib/json-decode.spec.ts index 85246a2..a80c602 100644 --- a/src/lib/json-decode.spec.ts +++ b/src/lib/json-decode.spec.ts @@ -4,15 +4,15 @@ import { bigint, bool, Decoder, - DecoderError, + DecoderError, enumerator, field, float, int, nullable, number, optional, - string, -} from './json-decode'; + string +} from "./json-decode"; describe('bool', () => { it('decodes a boolean', () => { @@ -125,6 +125,55 @@ describe('nullable', () => { }); }); +describe('enumerator', () => { + + describe('when the enum is a string enum', () => { + enum Choice { + carrot = 'carrot', + stick = 'stick' + } + + it('decodes the enum', () => { + expect(enumerator(Choice)('carrot')).toEqual(Choice.carrot); + }); + + it('throws when the value is not a member of the enum', () => { + expect(() => enumerator(Choice)('banana')).toThrowError(DecoderError); + }); + }); + + describe('when the enum is a numeric enum', () => { + enum Choice { + carrot = 0, + stick =1 + } + + it('decodes the enum', () => { + expect(enumerator(Choice)('carrot')).toEqual(Choice.carrot); + }); + + it('throws when the value is not a member of the enum', () => { + expect(() => enumerator(Choice)('banana')).toThrowError(DecoderError); + }); + }); + + + describe('when the enum has no values assigned', () => { + enum Choice { + carrot , + stick + } + + it('decodes the enum', () => { + expect(enumerator(Choice)('carrot')).toEqual(Choice.carrot); + }); + + it('throws when the value is not a member of the enum', () => { + expect(() => enumerator(Choice)('banana')).toThrowError(DecoderError); + }); + }); +}) + describe('decoding a complex object', () => { type Blob = { name: string; diff --git a/src/lib/json-decode.ts b/src/lib/json-decode.ts index cc44752..58c5879 100644 --- a/src/lib/json-decode.ts +++ b/src/lib/json-decode.ts @@ -114,3 +114,11 @@ export const nullable = (decoder: Decoder): Decoder => (json) => json === null ? json : decoder(json); + +export const enumerator = (enumType: T) => (json: unknown): T[keyof T] => { + const value = (enumType as any)[json as any]; + if (value === undefined ) { + throw new DecoderError(`Expected enum value, got ${JSON.stringify(json)}`); + } + return value; +} \ No newline at end of file From 365fed68c98b4a228b0907d5abd1541c9386a3a8 Mon Sep 17 00:00:00 2001 From: leow93 Date: Wed, 11 Oct 2023 22:35:35 +0100 Subject: [PATCH 2/2] feat: update docs and types --- README.md | 25 +++++++++++++++++++++++++ src/index.d.ts | 2 ++ src/index.ts | 3 ++- src/lib/json-decode.spec.ts | 10 +++++----- src/lib/json-decode.ts | 11 ++++++++--- 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 893df5b..d084cab 100644 --- a/README.md +++ b/README.md @@ -138,3 +138,28 @@ import { Decoder, array, field, number, string } from "json-decode"; array(number)([1, 2, 3]); // [1, 2, 3] array(number)([1, 2, '3']); // throws a DecodeError ``` + +### Decoding a TypeScript enum + +```typescript +import { enumerator } from "json-decode"; + +enum Choice { + carrot = 'carrot', + stick = 'stick' +} + +enumerator(Choice)('carrot'); // Choice.carrot +enumerator(Choice)('stick'); // Choice.stick +enumerator(Choice)('banana'); // throws a DecodeError + +enum Fruits { + apple, + banana, + pear, +} + +enumerator(Fruits)(0); // Fruits.apple +enumerator(Fruits)(1) // Fruits.banana +enumerator(Fruits)(3) // throws a DecodeError +``` diff --git a/src/index.d.ts b/src/index.d.ts index a2ce781..c552db5 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -25,3 +25,5 @@ export function nullable(decoder: Decoder): Decoder; export function optional(decoder: Decoder): Decoder; export const string: Decoder; + +export const enumerator: (enumObject: T) => Decoder; diff --git a/src/index.ts b/src/index.ts index 8fad885..49ef372 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,11 +4,12 @@ export { bool, Decoder, DecoderError, + enumerator, int, field, number, float, nullable, optional, - string + string, } from './lib/json-decode' diff --git a/src/lib/json-decode.spec.ts b/src/lib/json-decode.spec.ts index a80c602..b8ccf46 100644 --- a/src/lib/json-decode.spec.ts +++ b/src/lib/json-decode.spec.ts @@ -145,11 +145,11 @@ describe('enumerator', () => { describe('when the enum is a numeric enum', () => { enum Choice { carrot = 0, - stick =1 + stick = 1 } it('decodes the enum', () => { - expect(enumerator(Choice)('carrot')).toEqual(Choice.carrot); + expect(enumerator(Choice)(0)).toEqual(Choice.carrot); }); it('throws when the value is not a member of the enum', () => { @@ -164,12 +164,12 @@ describe('enumerator', () => { stick } - it('decodes the enum', () => { - expect(enumerator(Choice)('carrot')).toEqual(Choice.carrot); + it('decodes the enum using the value', () => { + expect(enumerator(Choice)(1)).toEqual(Choice.stick); }); it('throws when the value is not a member of the enum', () => { - expect(() => enumerator(Choice)('banana')).toThrowError(DecoderError); + expect(() => enumerator(Choice)(3)).toThrowError(DecoderError); }); }); }) diff --git a/src/lib/json-decode.ts b/src/lib/json-decode.ts index 58c5879..1e36cd5 100644 --- a/src/lib/json-decode.ts +++ b/src/lib/json-decode.ts @@ -116,9 +116,14 @@ export const nullable = json === null ? json : decoder(json); export const enumerator = (enumType: T) => (json: unknown): T[keyof T] => { - const value = (enumType as any)[json as any]; - if (value === undefined ) { + const entries = Object.entries(enumType as any); + const entry = entries.find(([, v]) => v === json); + if (entry === undefined) { throw new DecoderError(`Expected enum value, got ${JSON.stringify(json)}`); } - return value; + const value = entry[1]; + if (value === undefined) { + throw new DecoderError(`Expected enum value, got ${JSON.stringify(json)}`); + } + return value as T[keyof T]; } \ No newline at end of file