Architecture simplification: stop using decorators which adds unnecessary complexity and some compilation bugs.
+ Add a property definition class. + Add some definition functions on models, which have to be redefined when implementing a new model. - Remove decorators.
This commit is contained in:
parent
13072b453f
commit
1c3c87a4a6
8 changed files with 215 additions and 187 deletions
118
README.md
118
README.md
|
@ -4,21 +4,22 @@
|
||||||
|
|
||||||
Sharkitek is a Javascript / TypeScript library designed to ease development of client-side models.
|
Sharkitek is a Javascript / TypeScript library designed to ease development of client-side models.
|
||||||
|
|
||||||
With Sharkitek, you define the architecture of your models by applying decorators (which define their type) on your class properties.
|
With Sharkitek, you define the architecture of your models by specifying their properties and their types.
|
||||||
Then, you can use the defined methods like `serialize`, `deserialize` or `serializeDiff`.
|
Then, you can use the defined methods like `serialize`, `deserialize` or `serializeDiff`.
|
||||||
|
|
||||||
Sharkitek makes use of decorators as defined in the [TypeScript Reference](https://www.typescriptlang.org/docs/handbook/decorators.html).
|
|
||||||
Due to the way decorators work, you must always set a value to your properties when you declare them, even if this value is `undefined`.
|
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Example extends Model
|
class Example extends Model<Example>
|
||||||
{
|
{
|
||||||
@Property(SNumeric)
|
id: number;
|
||||||
@Identifier
|
name: string;
|
||||||
id: number = undefined;
|
|
||||||
|
|
||||||
@Property(SString)
|
protected SDefinition(): ModelDefinition<Example>
|
||||||
name: string = undefined;
|
{
|
||||||
|
return {
|
||||||
|
id: SDefine(SNumeric),
|
||||||
|
name: SDefine(SString),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -30,53 +31,60 @@ class Example extends Model
|
||||||
/**
|
/**
|
||||||
* A person.
|
* A person.
|
||||||
*/
|
*/
|
||||||
class Person extends Model
|
class Person extends Model<Person>
|
||||||
{
|
{
|
||||||
@Property(SNumeric)
|
id: number;
|
||||||
@Identifier
|
name: string;
|
||||||
id: number = undefined;
|
firstName: string;
|
||||||
|
email: string;
|
||||||
@Property(SString)
|
createdAt: Date;
|
||||||
name: string = undefined;
|
|
||||||
|
|
||||||
@Property(SString)
|
|
||||||
firstName: string = undefined;
|
|
||||||
|
|
||||||
@Property(SString)
|
|
||||||
email: string = undefined;
|
|
||||||
|
|
||||||
@Property(SDate)
|
|
||||||
createdAt: Date = undefined;
|
|
||||||
|
|
||||||
@Property(SBool)
|
|
||||||
active: boolean = true;
|
active: boolean = true;
|
||||||
|
|
||||||
|
protected SIdentifier(): ModelIdentifier<Person>
|
||||||
|
{
|
||||||
|
return "id";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SDefinition(): ModelDefinition<Person>
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
name: SDefine(SString),
|
||||||
|
firstName: SDefine(SString),
|
||||||
|
email: SDefine(SString),
|
||||||
|
createdAt: SDefine(SDate),
|
||||||
|
active: SDefine(SBool),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Important**: You _must_ set a value to all your defined properties. If there is no set value, the decorator will not
|
|
||||||
be applied instantly on object initialization and the deserialization will not work properly.
|
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
/**
|
/**
|
||||||
* An article.
|
* An article.
|
||||||
*/
|
*/
|
||||||
class Article extends Model
|
class Article extends Model<Article>
|
||||||
{
|
{
|
||||||
@Property(SNumeric)
|
id: number;
|
||||||
@Identifier
|
title: string;
|
||||||
id: number = undefined;
|
|
||||||
|
|
||||||
@Property(SString)
|
|
||||||
title: string = undefined;
|
|
||||||
|
|
||||||
@Property(SArray(SModel(Author)))
|
|
||||||
authors: Author[] = [];
|
authors: Author[] = [];
|
||||||
|
text: string;
|
||||||
|
evaluation: number;
|
||||||
|
|
||||||
@Property(SString)
|
protected SIdentifier(): ModelIdentifier<Article>
|
||||||
text: string = undefined;
|
{
|
||||||
|
return "id";
|
||||||
|
}
|
||||||
|
|
||||||
@Property(SDecimal)
|
protected SDefinition(): ModelDefinition<Article>
|
||||||
evaluation: number = undefined;
|
{
|
||||||
|
return {
|
||||||
|
id: SDefine(SNumeric),
|
||||||
|
title: SDefine(SString),
|
||||||
|
authors: SDefine(SArray(SModel(Author))),
|
||||||
|
text: SDefine(SString),
|
||||||
|
evaluation: SDefine(SDecimal),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -99,10 +107,16 @@ Sharkitek defines some basic types by default, in these classes:
|
||||||
When you are defining a Sharkitek property, you must provide its type by instantiating one of these classes.
|
When you are defining a Sharkitek property, you must provide its type by instantiating one of these classes.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Example extends Model
|
class Example extends Model<Example>
|
||||||
{
|
{
|
||||||
@Property(new StringType())
|
foo: string;
|
||||||
foo: string = undefined;
|
|
||||||
|
protected SDefinition(): ModelDefinition<Example>
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
foo: new Definition(new StringType()),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -125,10 +139,16 @@ multiple functions or constants when predefined parameters. (For example, we cou
|
||||||
be a variable similar to `SArray(SString)`.)
|
be a variable similar to `SArray(SString)`.)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Example extends Model
|
class Example extends Model<Example>
|
||||||
{
|
{
|
||||||
@Property(SString)
|
|
||||||
foo: string = undefined;
|
foo: string = undefined;
|
||||||
|
|
||||||
|
protected SDefinition(): ModelDefinition<Example>
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
foo: SDefine(SString),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@sharkitek/core",
|
"name": "@sharkitek/core",
|
||||||
"version": "1.3.1",
|
"version": "2.0.0",
|
||||||
"description": "Sharkitek core models library.",
|
"description": "Sharkitek core models library.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"sharkitek",
|
"sharkitek",
|
||||||
|
@ -24,9 +24,6 @@
|
||||||
"files": [
|
"files": [
|
||||||
"lib/**/*"
|
"lib/**/*"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
|
||||||
"reflect-metadata": "^0.1.13"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^28.1.6",
|
"@types/jest": "^28.1.6",
|
||||||
"esbuild": "^0.15.8",
|
"esbuild": "^0.15.8",
|
||||||
|
|
34
src/Model/Definition.ts
Normal file
34
src/Model/Definition.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import {Type} from "./Types/Type";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options of a definition.
|
||||||
|
*/
|
||||||
|
export interface DefinitionOptions<SerializedType, SharkitekType>
|
||||||
|
{ //TODO implement some options, like `mandatory`.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Sharkitek model property definition.
|
||||||
|
*/
|
||||||
|
export class Definition<SerializedType, SharkitekType>
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Initialize a property definition with the given type and options.
|
||||||
|
* @param type - The model property type.
|
||||||
|
* @param options - Property definition options.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
public type: Type<SerializedType, SharkitekType>,
|
||||||
|
public options: DefinitionOptions<SerializedType, SharkitekType> = {},
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a property definition with the given type and options.
|
||||||
|
* @param type - The model property type.
|
||||||
|
* @param options - Property definition options.
|
||||||
|
*/
|
||||||
|
export function SDefine<SerializedType, SharkitekType>(type: Type<SerializedType, SharkitekType>, options: DefinitionOptions<SerializedType, SharkitekType> = {})
|
||||||
|
{
|
||||||
|
return new Definition(type, options);
|
||||||
|
}
|
|
@ -1,89 +1,50 @@
|
||||||
import {Type} from "./Types/Type";
|
import {Definition} from "./Definition";
|
||||||
import "reflect-metadata";
|
|
||||||
import {ConstructorOf} from "./Types/ModelType";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key of Sharkitek property metadata.
|
* Model properties definition type.
|
||||||
*/
|
*/
|
||||||
const sharkitekMetadataKey = Symbol("sharkitek");
|
export type ModelDefinition<T> = Partial<Record<keyof T, Definition<unknown, unknown>>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key of Sharkitek model identifier.
|
* Model identifier type.
|
||||||
*/
|
*/
|
||||||
const modelIdentifierMetadataKey = Symbol("modelIdentifier");
|
export type ModelIdentifier<T> = keyof T;
|
||||||
|
|
||||||
/**
|
|
||||||
* Sharkitek property metadata interface.
|
|
||||||
*/
|
|
||||||
interface SharkitekMetadataInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Property type instance.
|
|
||||||
*/
|
|
||||||
type: Type<any, any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class decorator for Sharkitek models.
|
|
||||||
*/
|
|
||||||
export function Sharkitek(constructor: Function)
|
|
||||||
{
|
|
||||||
/*return class extends (constructor as FunctionConstructor) {
|
|
||||||
constructor()
|
|
||||||
{
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
};*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Property decorator to define a Sharkitek model identifier.
|
|
||||||
*/
|
|
||||||
export function Identifier(obj: Model, propertyName: string): void
|
|
||||||
{
|
|
||||||
// Register the current property as identifier of the current model object.
|
|
||||||
Reflect.defineMetadata(modelIdentifierMetadataKey, propertyName, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Property decorator for Sharkitek models properties.
|
|
||||||
* @param type - Type of the property.
|
|
||||||
*/
|
|
||||||
export function Property<SerializedType, SharkitekType>(type: Type<SerializedType, SharkitekType>): PropertyDecorator
|
|
||||||
{
|
|
||||||
// Return the decorator function.
|
|
||||||
return (obj: ConstructorOf<Model>, propertyName) => {
|
|
||||||
// Initializing property metadata.
|
|
||||||
const metadata: SharkitekMetadataInterface = {
|
|
||||||
type: type,
|
|
||||||
};
|
|
||||||
// Set property metadata.
|
|
||||||
Reflect.defineMetadata(sharkitekMetadataKey, metadata, obj, propertyName);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Sharkitek model.
|
* A Sharkitek model.
|
||||||
*/
|
*/
|
||||||
export abstract class Model
|
export abstract class Model<THIS>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Get the Sharkitek model identifier.
|
* Model properties definition function.
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
private getModelIdentifier(): string
|
protected abstract SDefinition(): ModelDefinition<THIS>;
|
||||||
{
|
|
||||||
return Reflect.getMetadata(modelIdentifierMetadataKey, this);
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Get the Sharkitek metadata of the property.
|
* Return the name of the model identifier property.
|
||||||
* @param propertyName - The name of the property for which to get metadata.
|
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
private getPropertyMetadata(propertyName: string): SharkitekMetadataInterface
|
protected SIdentifier(): ModelIdentifier<THIS>
|
||||||
{
|
{
|
||||||
return Reflect.getMetadata(sharkitekMetadataKey, this, propertyName);
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get given property definition.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
protected getPropertyDefinition(propertyName: string): Definition<unknown, unknown>
|
||||||
|
{
|
||||||
|
return (this.SDefinition() as any)?.[propertyName];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of the model properties.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
protected getProperties(): string[]
|
||||||
|
{
|
||||||
|
return Object.keys(this.SDefinition());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calling a function for a defined property.
|
* Calling a function for a defined property.
|
||||||
* @param propertyName - The property for which to check definition.
|
* @param propertyName - The property for which to check definition.
|
||||||
|
@ -91,15 +52,15 @@ export abstract class Model
|
||||||
* @param notProperty - The function called when the property is not defined.
|
* @param notProperty - The function called when the property is not defined.
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected propertyWithMetadata(propertyName: string, callback: (propertyMetadata: SharkitekMetadataInterface) => void, notProperty: () => void = () => {}): unknown
|
protected propertyWithDefinition(propertyName: string, callback: (propertyDefinition: Definition<unknown, unknown>) => void, notProperty: () => void = () => {}): unknown
|
||||||
{
|
{
|
||||||
// Getting the current property metadata.
|
// Getting the current property definition.
|
||||||
const propertyMetadata = this.getPropertyMetadata(propertyName);
|
const propertyDefinition = this.getPropertyDefinition(propertyName);
|
||||||
if (propertyMetadata)
|
if (propertyDefinition)
|
||||||
// Metadata are defined, calling the right callback.
|
// There is a definition for the current property, calling the right callback.
|
||||||
return callback(propertyMetadata);
|
return callback(propertyDefinition);
|
||||||
else
|
else
|
||||||
// Metadata are not defined, calling the right callback.
|
// No definition for the given property, calling the right callback.
|
||||||
return notProperty();
|
return notProperty();
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -107,19 +68,16 @@ export abstract class Model
|
||||||
* @param callback - The function to call.
|
* @param callback - The function to call.
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected forEachModelProperty(callback: (propertyName: string, propertyMetadata: SharkitekMetadataInterface) => unknown): any|void
|
protected forEachModelProperty(callback: (propertyName: string, propertyDefinition: Definition<unknown, unknown>) => unknown): any|void
|
||||||
{
|
{
|
||||||
for (const propertyName of Object.keys(this))
|
for (const propertyName of this.getProperties())
|
||||||
{ // For each property, checking that its type is defined and calling the callback with its type.
|
{ // For each property, checking that its type is defined and calling the callback with its type.
|
||||||
const result = this.propertyWithMetadata(propertyName, (propertyMetadata) => {
|
const result = this.propertyWithDefinition(propertyName, (propertyDefinition) => {
|
||||||
// If the property is defined, calling the function with the property name and metadata.
|
// If the property is defined, calling the function with the property name and definition.
|
||||||
const result = callback(propertyName, propertyMetadata);
|
const result = callback(propertyName, propertyDefinition);
|
||||||
|
|
||||||
// If there is a return value, returning it directly (loop is broken).
|
// If there is a return value, returning it directly (loop is broken).
|
||||||
if (typeof result !== "undefined") return result;
|
if (typeof result !== "undefined") return result;
|
||||||
|
|
||||||
// Update metadata if they have changed.
|
|
||||||
Reflect.defineMetadata(sharkitekMetadataKey, propertyMetadata, this, propertyName);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// If there is a return value, returning it directly (loop is broken).
|
// If there is a return value, returning it directly (loop is broken).
|
||||||
|
@ -153,12 +111,12 @@ export abstract class Model
|
||||||
*/
|
*/
|
||||||
isDirty(): boolean
|
isDirty(): boolean
|
||||||
{
|
{
|
||||||
return this.forEachModelProperty((propertyName, propertyMetadata) => (
|
return this.forEachModelProperty((propertyName, propertyDefinition) => (
|
||||||
// For each property, checking if it is different.
|
// For each property, checking if it is different.
|
||||||
propertyMetadata.type.propertyHasChanged(this._originalProperties[propertyName], (this as any)[propertyName])
|
propertyDefinition.type.propertyHasChanged(this._originalProperties[propertyName], (this as any)[propertyName])
|
||||||
// There is a difference, we should return false.
|
// There is a difference, we should return false.
|
||||||
? true
|
? true
|
||||||
// There is not difference, returning nothing.
|
// There is no difference, returning nothing.
|
||||||
: undefined
|
: undefined
|
||||||
)) === true;
|
)) === true;
|
||||||
}
|
}
|
||||||
|
@ -168,7 +126,7 @@ export abstract class Model
|
||||||
*/
|
*/
|
||||||
getIdentifier(): unknown
|
getIdentifier(): unknown
|
||||||
{
|
{
|
||||||
return (this as any)[this.getModelIdentifier()];
|
return (this as any)[this.SIdentifier()];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -176,10 +134,10 @@ export abstract class Model
|
||||||
*/
|
*/
|
||||||
resetDiff()
|
resetDiff()
|
||||||
{
|
{
|
||||||
this.forEachModelProperty((propertyName, propertyMetadata) => {
|
this.forEachModelProperty((propertyName, propertyDefinition) => {
|
||||||
// For each property, set its original value to its current property value.
|
// For each property, set its original value to its current property value.
|
||||||
this._originalProperties[propertyName] = (this as any)[propertyName];
|
this._originalProperties[propertyName] = (this as any)[propertyName];
|
||||||
propertyMetadata.type.resetDiff((this as any)[propertyName]);
|
propertyDefinition.type.resetDiff((this as any)[propertyName]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -190,12 +148,12 @@ export abstract class Model
|
||||||
// Creating a serialized object.
|
// Creating a serialized object.
|
||||||
const serializedDiff: any = {};
|
const serializedDiff: any = {};
|
||||||
|
|
||||||
this.forEachModelProperty((propertyName, propertyMetadata) => {
|
this.forEachModelProperty((propertyName, propertyDefinition) => {
|
||||||
// For each defined model property, adding it to the serialized object if it has changed.
|
// For each defined model property, adding it to the serialized object if it has changed.
|
||||||
if (this.getModelIdentifier() == propertyName
|
if (this.SIdentifier() == propertyName
|
||||||
|| propertyMetadata.type.propertyHasChanged(this._originalProperties[propertyName], (this as any)[propertyName]))
|
|| propertyDefinition.type.propertyHasChanged(this._originalProperties[propertyName], (this as any)[propertyName]))
|
||||||
// Adding the current property to the serialized object if it is the identifier or its value has changed.
|
// Adding the current property to the serialized object if it is the identifier or its value has changed.
|
||||||
serializedDiff[propertyName] = propertyMetadata.type.serializeDiff((this as any)[propertyName]);
|
serializedDiff[propertyName] = propertyDefinition.type.serializeDiff((this as any)[propertyName]);
|
||||||
})
|
})
|
||||||
|
|
||||||
return serializedDiff; // Returning the serialized object.
|
return serializedDiff; // Returning the serialized object.
|
||||||
|
@ -224,9 +182,9 @@ export abstract class Model
|
||||||
// Creating a serialized object.
|
// Creating a serialized object.
|
||||||
const serializedObject: any = {};
|
const serializedObject: any = {};
|
||||||
|
|
||||||
this.forEachModelProperty((propertyName, propertyMetadata) => {
|
this.forEachModelProperty((propertyName, propertyDefinition) => {
|
||||||
// For each defined model property, adding it to the serialized object.
|
// For each defined model property, adding it to the serialized object.
|
||||||
serializedObject[propertyName] = propertyMetadata.type.serialize((this as any)[propertyName]);
|
serializedObject[propertyName] = propertyDefinition.type.serialize((this as any)[propertyName]);
|
||||||
});
|
});
|
||||||
|
|
||||||
return serializedObject; // Returning the serialized object.
|
return serializedObject; // Returning the serialized object.
|
||||||
|
@ -237,16 +195,16 @@ export abstract class Model
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected parse(): void
|
protected parse(): void
|
||||||
{} // Nothing by default. TODO: create a event system to create functions like "beforeDeserialization" or "afterDeserialization".
|
{} // Nothing by default. TODO: create an event system to create functions like "beforeDeserialization" or "afterDeserialization".
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize the model.
|
* Deserialize the model.
|
||||||
*/
|
*/
|
||||||
deserialize(serializedObject: any): this
|
deserialize(serializedObject: any): THIS
|
||||||
{
|
{
|
||||||
this.forEachModelProperty((propertyName, propertyMetadata) => {
|
this.forEachModelProperty((propertyName, propertyDefinition) => {
|
||||||
// For each defined model property, assigning its deserialized value to the model.
|
// For each defined model property, assigning its deserialized value to the model.
|
||||||
(this as any)[propertyName] = propertyMetadata.type.deserialize(serializedObject[propertyName]);
|
(this as any)[propertyName] = propertyDefinition.type.deserialize(serializedObject[propertyName]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Reset original property values.
|
// Reset original property values.
|
||||||
|
@ -254,6 +212,6 @@ export abstract class Model
|
||||||
|
|
||||||
this._originalObject = serializedObject; // The model is not a new one, but loaded from a deserialized one.
|
this._originalObject = serializedObject; // The model is not a new one, but loaded from a deserialized one.
|
||||||
|
|
||||||
return this; // Returning this, after deserialization.
|
return this as unknown as THIS; // Returning this, after deserialization.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ export type ConstructorOf<T> = { new(): T; }
|
||||||
/**
|
/**
|
||||||
* Type of a Sharkitek model value.
|
* Type of a Sharkitek model value.
|
||||||
*/
|
*/
|
||||||
export class ModelType<M extends Model> extends Type<any, M>
|
export class ModelType<M extends Model<M>> extends Type<any, M>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Constructs a new model type of a Sharkitek model property.
|
* Constructs a new model type of a Sharkitek model property.
|
||||||
|
@ -49,7 +49,7 @@ export class ModelType<M extends Model> extends Type<any, M>
|
||||||
* Type of a Sharkitek model value.
|
* Type of a Sharkitek model value.
|
||||||
* @param modelConstructor - Constructor of the model.
|
* @param modelConstructor - Constructor of the model.
|
||||||
*/
|
*/
|
||||||
export function SModel<M extends Model>(modelConstructor: ConstructorOf<M>)
|
export function SModel<M extends Model<M>>(modelConstructor: ConstructorOf<M>)
|
||||||
{
|
{
|
||||||
return new ModelType(modelConstructor);
|
return new ModelType(modelConstructor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
export * from "./Model/Model";
|
export * from "./Model/Model";
|
||||||
|
|
||||||
|
export * from "./Model/Definition";
|
||||||
|
|
||||||
export * from "./Model/Types/Type";
|
export * from "./Model/Types/Type";
|
||||||
export * from "./Model/Types/ArrayType";
|
export * from "./Model/Types/ArrayType";
|
||||||
export * from "./Model/Types/BoolType";
|
export * from "./Model/Types/BoolType";
|
||||||
|
|
|
@ -1,25 +1,38 @@
|
||||||
import {SArray, SDecimal, SModel, SNumeric, SString, SDate, SBool, Identifier, Model, Property} from "../src";
|
import {
|
||||||
|
SArray,
|
||||||
|
SDecimal,
|
||||||
|
SModel,
|
||||||
|
SNumeric,
|
||||||
|
SString,
|
||||||
|
SDate,
|
||||||
|
SBool,
|
||||||
|
Model,
|
||||||
|
ModelDefinition,
|
||||||
|
SDefine, ModelIdentifier
|
||||||
|
} from "../src";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Another test model.
|
* Another test model.
|
||||||
*/
|
*/
|
||||||
class Author extends Model
|
class Author extends Model<Author>
|
||||||
{
|
{
|
||||||
@Property(SString)
|
name: string;
|
||||||
name: string = undefined;
|
firstName: string;
|
||||||
|
email: string;
|
||||||
@Property(SString)
|
createdAt: Date;
|
||||||
firstName: string = undefined;
|
|
||||||
|
|
||||||
@Property(SString)
|
|
||||||
email: string = undefined;
|
|
||||||
|
|
||||||
@Property(SDate)
|
|
||||||
createdAt: Date = undefined;
|
|
||||||
|
|
||||||
@Property(SBool)
|
|
||||||
active: boolean = true;
|
active: boolean = true;
|
||||||
|
|
||||||
|
protected SDefinition(): ModelDefinition<Author>
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
name: SDefine(SString),
|
||||||
|
firstName: SDefine(SString),
|
||||||
|
email: SDefine(SString),
|
||||||
|
createdAt: SDefine(SDate),
|
||||||
|
active: SDefine(SBool),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
constructor(name: string = undefined, firstName: string = undefined, email: string = undefined, createdAt: Date = undefined)
|
constructor(name: string = undefined, firstName: string = undefined, email: string = undefined, createdAt: Date = undefined)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
@ -34,23 +47,29 @@ class Author extends Model
|
||||||
/**
|
/**
|
||||||
* A test model.
|
* A test model.
|
||||||
*/
|
*/
|
||||||
class Article extends Model
|
class Article extends Model<Article>
|
||||||
{
|
{
|
||||||
@Property(SNumeric)
|
id: number;
|
||||||
@Identifier
|
title: string;
|
||||||
id: number = undefined;
|
|
||||||
|
|
||||||
@Property(SString)
|
|
||||||
title: string = undefined;
|
|
||||||
|
|
||||||
@Property(SArray(SModel(Author)))
|
|
||||||
authors: Author[] = [];
|
authors: Author[] = [];
|
||||||
|
text: string;
|
||||||
|
evaluation: number;
|
||||||
|
|
||||||
@Property(SString)
|
protected SIdentifier(): ModelIdentifier<Article>
|
||||||
text: string = undefined;
|
{
|
||||||
|
return "id";
|
||||||
|
}
|
||||||
|
|
||||||
@Property(SDecimal)
|
protected SDefinition(): ModelDefinition<Article>
|
||||||
evaluation: number = undefined;
|
{
|
||||||
|
return {
|
||||||
|
id: SDefine(SNumeric),
|
||||||
|
title: SDefine(SString),
|
||||||
|
authors: SDefine(SArray(SModel(Author))),
|
||||||
|
text: SDefine(SString),
|
||||||
|
evaluation: SDefine(SDecimal),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
it("deserialize", () => {
|
it("deserialize", () => {
|
||||||
|
|
|
@ -10,8 +10,6 @@
|
||||||
"outDir": "./lib/",
|
"outDir": "./lib/",
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"experimentalDecorators": true,
|
|
||||||
"emitDecoratorMetadata": true,
|
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"declarationMap": true,
|
"declarationMap": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
|
|
Loading…
Reference in a new issue