From 1fa41ea3148783de77b86d362b8f76e10d532c9d Mon Sep 17 00:00:00 2001 From: Florin Mihalache Date: Thu, 7 Jan 2021 21:28:38 +0200 Subject: [PATCH] feat: map support --- .eslintrc.json | 4 +- src/revive.test.ts | 28 ++++++++++++++ src/revive.ts | 93 +++++++++++++++++++++++++++++++--------------- src/types.ts | 6 ++- 4 files changed, 99 insertions(+), 32 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index a71cfd4..e98fe0e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -17,9 +17,9 @@ ], "rules": { "comma-dangle": "off", - "indent": ["error", 2], "linebreak-style": ["error", "unix"], "quotes": ["error", "single"], - "@typescript-eslint/no-explicit-any": "off" + "@typescript-eslint/no-explicit-any": "off", + "indent": ["error", 2, {"SwitchCase": 1}] } } diff --git a/src/revive.test.ts b/src/revive.test.ts index 7efcc4d..02375f9 100644 --- a/src/revive.test.ts +++ b/src/revive.test.ts @@ -287,4 +287,32 @@ describe('revive tests', () => { const data = 'true' assert.throw(() => revive(data, Number), 'expected schema type to be Boolean, got Number') }) + + it('should revive a map correctly', function () { + class Prop { + value = 0 + getValue() { return this.value } + } + class Person { + name = '' + props: {[key: string]: Prop} = {} + getName() { return this.name } + getProps() { return this.props } + static getRevivalSchema(): RevivalSchema { + return { + type: Person, + properties: { + props: { + map: Prop + } + } + } + } + } + const data= '{"name": "John", "props": {"foo": {"value": 1}, "bar": {"value": 2}}}' + const p = revive(data, Person) + assert.equal(p.getName(), 'John') + assert.equal(p.getProps()['foo'].getValue(), 1) + assert.equal(p.getProps()['bar'].getValue(), 2) + }); }) diff --git a/src/revive.ts b/src/revive.ts index 4180499..28a8dd1 100644 --- a/src/revive.ts +++ b/src/revive.ts @@ -1,13 +1,12 @@ import { defaultReviveOptions, RevivalArraySchema, - RevivalConstructor, + RevivalConstructor, RevivalMapSchema, RevivalObjectSchema, RevivalOptions, RevivalSchema, RevivalSchemaProvider, } from './types'; - function isObjectSchema(schema: any): schema is RevivalObjectSchema { return 'type' in schema && 'properties' in schema } @@ -16,6 +15,10 @@ function isArraySchema(schema: any): schema is RevivalArraySchema { return 'items' in schema } +function isMapSchema(schema: any): schema is RevivalMapSchema { + return 'map' in schema +} + function isSchemaProvider(obj: any): obj is RevivalSchemaProvider { return typeof obj['getRevivalSchema'] === 'function' } @@ -25,6 +28,7 @@ type Revivable = string | number | {[key: string]: any} | any[] export function revive(data: Revivable, schema: RevivalObjectSchema, options?: RevivalOptions): T; export function revive(data: Revivable, schema: RevivalArraySchema, options?: RevivalOptions): T[]; export function revive(data: Revivable, schema: RevivalConstructor, options?: RevivalOptions): T; +export function revive(data: Revivable, schema: RevivalMapSchema, options?: RevivalOptions): {[key: string]: T}; export function revive(data: Revivable, schema: RevivalSchema,options: RevivalOptions = defaultReviveOptions): T { let obj: any @@ -46,7 +50,11 @@ function reviveAny(data: any, schema: RevivalSchema, options: RevivalOption if (isArraySchema(schema)) { return reviveArrayAny(data, schema, options) } else { - return reviveConstructorAny(data, schema, options) + if (isMapSchema(schema)) { + return reviveMapAny(data, schema, options) + } else { + return reviveConstructorAny(data, schema, options) + } } } } @@ -62,32 +70,32 @@ function reviveObjectAny(data: any, schema: RevivalObjectSchema, options: R } switch(typeof data) { - case 'number': - if (schema.type !== Number) { - throw new TypeError(`expected schema type to be Number, got ${schema.type.name}`) - } - return data - case 'bigint': - if (schema.type !== Number) { - throw new TypeError(`expected schema type to be Number, got ${schema.type.name}`) - } - return data - case 'boolean': - if (schema.type !== Boolean) { - throw new TypeError(`expected schema type to be Boolean, got ${schema.type.name}`) - } - return data - case 'function': - throw new TypeError('`function` is not supported') - case 'string': - if (schema.type !== String) { - throw new TypeError(`expected schema type to be String, got ${schema.type.name}`) - } - return data - case 'symbol': - throw new TypeError('`symbol` is not supported') - case 'undefined': - return data + case 'number': + if (schema.type !== Number) { + throw new TypeError(`expected schema type to be Number, got ${schema.type.name}`) + } + return data + case 'bigint': + if (schema.type !== Number) { + throw new TypeError(`expected schema type to be Number, got ${schema.type.name}`) + } + return data + case 'boolean': + if (schema.type !== Boolean) { + throw new TypeError(`expected schema type to be Boolean, got ${schema.type.name}`) + } + return data + case 'function': + throw new TypeError('`function` is not supported') + case 'string': + if (schema.type !== String) { + throw new TypeError(`expected schema type to be String, got ${schema.type.name}`) + } + return data + case 'symbol': + throw new TypeError('`symbol` is not supported') + case 'undefined': + return data } if (Array.isArray(data)) { @@ -141,6 +149,33 @@ function reviveArrayAny(data: any, schema: RevivalArraySchema, options: Rev return ret } +function reviveMapAny(data: any, schema: RevivalMapSchema, options: RevivalOptions): any { + const ret: {[key: string]: any} = {} + + switch(typeof data) { + case 'number': + case 'bigint': + case 'boolean': + case 'function': + case 'string': + case 'symbol': + case 'undefined': + throw new TypeError(`expected an object; got an ${typeof data}`) + } + + if (Array.isArray(data)) { + throw new TypeError('expected object, got array') + } + + /* assign values */ + for (const prop of Object.keys(data)) { + ret[prop] = reviveAny(data[prop], schema.map, options) + } + + return ret +} + + function reviveConstructorAny(data: any, objConstructor: RevivalConstructor, options: RevivalOptions): any { if (isSchemaProvider(objConstructor)) { return reviveAny(data, objConstructor.getRevivalSchema(), options) diff --git a/src/types.ts b/src/types.ts index 10e30b5..c32f41a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -23,7 +23,11 @@ export interface RevivalArraySchema { items: RevivalSchema } -export type RevivalSchema = RevivalConstructor | RevivalObjectSchema | RevivalArraySchema +export interface RevivalMapSchema { + map: RevivalSchema +} + +export type RevivalSchema = RevivalConstructor | RevivalObjectSchema | RevivalArraySchema | RevivalMapSchema export interface RevivalSchemaProvider { getRevivalSchema(): RevivalSchema