Updates to @Strong()
This commit is contained in:
@@ -1,9 +1,15 @@
|
||||
export const STRONG_CLASS_KEY = 'serde:strong_class';
|
||||
export const STRONG_ERROR_MESSAGE = 'Strong Type mismatch';
|
||||
|
||||
export function Strong(): Function {
|
||||
return function(target: any) {
|
||||
if (!Reflect.hasMetadata(STRONG_CLASS_KEY, target)) {
|
||||
Reflect.defineMetadata(STRONG_CLASS_KEY, 'is_strong_typed', target);
|
||||
}
|
||||
export function Strong<T>(type: string | T): PropertyDecorator {
|
||||
return function(target: Object, propertyKey: PropertyKey) {
|
||||
Reflect.defineMetadata(
|
||||
STRONG_CLASS_KEY,
|
||||
{
|
||||
...Reflect.getMetadata(STRONG_CLASS_KEY, target) || {},
|
||||
[propertyKey]: type
|
||||
},
|
||||
target
|
||||
);
|
||||
}
|
||||
}
|
||||
25
src/serde.ts
25
src/serde.ts
@@ -1,4 +1,4 @@
|
||||
import { EXCLUDED_PROPERTIES_KEY, PLUCK_PROPERTIES_KEY } from './decorators';
|
||||
import { EXCLUDED_PROPERTIES_KEY, PLUCK_PROPERTIES_KEY, STRONG_CLASS_KEY, STRONG_ERROR_MESSAGE } from './decorators';
|
||||
|
||||
export abstract class Serde<S> extends Object {
|
||||
protected removeableProperty(key: string): boolean {
|
||||
@@ -12,7 +12,27 @@ export abstract class Serde<S> extends Object {
|
||||
return value instanceof Serde ? value.serialize() : value;
|
||||
}
|
||||
|
||||
private checkStrongTypes(input: Partial<S>): boolean {
|
||||
const strongTyped = Reflect.getMetadata(STRONG_CLASS_KEY, this);
|
||||
if (!strongTyped) {
|
||||
return true;
|
||||
}
|
||||
return Object.entries(strongTyped).every(([key, type]) => {
|
||||
if (typeof type === 'string') {
|
||||
return typeof input[key] === type;
|
||||
}
|
||||
if (typeof type === 'object') {
|
||||
const clazz = type as any;
|
||||
return input[key] instanceof clazz;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
copyWith<C extends S>(input: { [P in keyof Partial<C>]: any }): this {
|
||||
if (!this.checkStrongTypes(input)) {
|
||||
throw Error(STRONG_ERROR_MESSAGE);
|
||||
}
|
||||
return Object.assign(
|
||||
Object.create(Object.getPrototypeOf(this)),
|
||||
this,
|
||||
@@ -25,9 +45,12 @@ export abstract class Serde<S> extends Object {
|
||||
customSerialize?<C extends S>(input: { [P in keyof Partial<C>]: any }): any;
|
||||
|
||||
deserialize(input: Partial<S>): this {
|
||||
if (this.checkStrongTypes(input)) {
|
||||
Object.assign(this, input);
|
||||
return this;
|
||||
}
|
||||
throw Error(STRONG_ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
serialize() {
|
||||
const pluckProperties = Reflect.getMetadata(PLUCK_PROPERTIES_KEY, this) as {[key: string]: any};
|
||||
|
||||
67
tests/decorators/strong.spec.ts
Normal file
67
tests/decorators/strong.spec.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import 'reflect-metadata';
|
||||
import { Serde } from "src/serde";
|
||||
import { Strong } from 'src/decorators';
|
||||
|
||||
class StrongTestModel extends Serde<StrongTestModel> {
|
||||
name: string;
|
||||
date: Date;
|
||||
@Strong('string') description: string;
|
||||
}
|
||||
|
||||
class TestModel extends Serde<TestModel> {
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
class NestedStrongTestModel extends Serde<NestedStrongTestModel> {
|
||||
name: string;
|
||||
@Strong(TestModel) nested: TestModel;
|
||||
}
|
||||
|
||||
describe('@Strong() Decorator', () => {
|
||||
describe('simple strong model', () => {
|
||||
it('should set value nicely', () => {
|
||||
const testData: any = {
|
||||
name: 'test model',
|
||||
description: 'this is a test model',
|
||||
}
|
||||
expect(() => {
|
||||
new StrongTestModel().deserialize(testData)
|
||||
}).not.toThrow();
|
||||
expect(new StrongTestModel().deserialize(testData).description).toBe('this is a test model');
|
||||
});
|
||||
|
||||
it('should throw', () => {
|
||||
const testData: any = {
|
||||
name: 'test model',
|
||||
description: 1234,
|
||||
}
|
||||
expect(() => {
|
||||
new StrongTestModel().deserialize(testData)
|
||||
}).toThrow('Strong Type mismatch');
|
||||
});
|
||||
});
|
||||
|
||||
describe('nested strong model', () => {
|
||||
it('should set value nicely', () => {
|
||||
const testData: any = {
|
||||
name: 'test model',
|
||||
nested: new TestModel().deserialize({ key: 'foo', value: 'bar' })
|
||||
}
|
||||
expect(() => {
|
||||
new NestedStrongTestModel().deserialize(testData)
|
||||
}).not.toThrow();
|
||||
expect(new NestedStrongTestModel().deserialize(testData).name).toBe('test model');
|
||||
});
|
||||
|
||||
it('should throw', () => {
|
||||
const testData: any = {
|
||||
name: 'test model',
|
||||
nested: false
|
||||
}
|
||||
expect(() => {
|
||||
new StrongTestModel().deserialize(testData)
|
||||
}).toThrow('Strong Type mismatch');
|
||||
});
|
||||
})
|
||||
});
|
||||
Reference in New Issue
Block a user