Add models extension system.

This commit is contained in:
Madeorsk 2024-10-04 21:24:11 +02:00
parent 576338fa62
commit 4eb8b7d3bc
Signed by: Madeorsk
GPG key ID: 677E51CA765BB79F
5 changed files with 190 additions and 133 deletions

View file

@ -19,7 +19,7 @@
</p> </p>
<p align="center"> <p align="center">
<img alt="Version 3.0.2" src="https://img.shields.io/badge/version-3.0.2-blue" /> <img alt="Version 3.1.0" src="https://img.shields.io/badge/version-3.1.0-blue" />
</p> </p>
## Introduction ## Introduction

View file

@ -1,6 +1,6 @@
{ {
"name": "@sharkitek/core", "name": "@sharkitek/core",
"version": "3.0.2", "version": "3.1.0",
"description": "TypeScript library for well-designed model architectures.", "description": "TypeScript library for well-designed model architectures.",
"keywords": [ "keywords": [
"deserialization", "deserialization",

View file

@ -34,10 +34,20 @@ export type SerializedModel<Shape extends ModelShape> = {
*/ */
export type Model<Shape extends ModelShape, IdentifierType = unknown> = ModelDefinition<Shape, IdentifierType> & PropertiesModel<Shape>; export type Model<Shape extends ModelShape, IdentifierType = unknown> = ModelDefinition<Shape, IdentifierType> & PropertiesModel<Shape>;
/**
* Type of the extends function of model classes.
*/
export type ExtendsFunctionType<ModelType extends Model<Shape, IdentifierType<Shape, Identifier>>, Shape extends ModelShape, Identifier extends keyof Shape = any> =
<Extension extends object>(extension: ThisType<ModelType> & Extension) => ModelClass<ModelType & Extension, Shape, Identifier>;
/** /**
* Type of a model class. * Type of a model class.
*/ */
export type ModelClass<Shape extends ModelShape, Identifier extends keyof Shape = any> = ConstructorOf<Model<Shape, IdentifierType<Shape, Identifier>>>; export type ModelClass<ModelType extends Model<Shape, IdentifierType<Shape, Identifier>>, Shape extends ModelShape, Identifier extends keyof Shape = any> = (
ConstructorOf<ModelType> & {
extends: ExtendsFunctionType<ModelType, Shape, Identifier>;
}
);
/** /**
* Identifier type. * Identifier type.
@ -96,12 +106,14 @@ export interface ModelDefinition<Shape extends ModelShape, IdentifierType, Model
export function model<ModelType extends Model<Shape, IdentifierType<Shape, Identifier>>, Shape extends ModelShape, Identifier extends keyof Shape = any>( export function model<ModelType extends Model<Shape, IdentifierType<Shape, Identifier>>, Shape extends ModelShape, Identifier extends keyof Shape = any>(
shape: Shape, shape: Shape,
identifier?: Identifier, identifier?: Identifier,
): ConstructorOf<ModelType> ): ModelClass<ModelType, Shape, Identifier>
{ {
// Get shape entries. // Get shape entries.
const shapeEntries = Object.entries(shape) as [keyof Shape, UnknownDefinition][]; const shapeEntries = Object.entries(shape) as [keyof Shape, UnknownDefinition][];
return class GenericModel implements ModelDefinition<Shape, IdentifierType<Shape, Identifier>, ModelType> return withExtends(
// Initialize generic model class.
class GenericModel implements ModelDefinition<Shape, IdentifierType<Shape, Identifier>, ModelType>
{ {
constructor() constructor()
{ {
@ -232,5 +244,38 @@ export function model<ModelType extends Model<Shape, IdentifierType<Shape, Ident
return diff; // Return the difference. return diff; // Return the difference.
} }
} as unknown as ConstructorOf<ModelType>; } as unknown as ConstructorOf<ModelType>
);
}
/**
* Any Sharkitek model.
*/
export type AnyModel = Model<any, any>;
/**
* Any Sharkitek model class.
*/
export type AnyModelClass = ModelClass<AnyModel, any>;
/**
* Add extends function to a model class.
* @param genericModel The model class on which to add the extends function.
*/
function withExtends<ModelType extends Model<Shape, IdentifierType<Shape, Identifier>>, Shape extends ModelShape, Identifier extends keyof Shape = any>(
genericModel: ConstructorOf<ModelType>
): ModelClass<ModelType, Shape, Identifier>
{
return Object.assign(
genericModel,
{ // Extends function definition.
extends<Extension extends object>(extension: Extension): ModelClass<ModelType & Extension, Shape, Identifier>
{
// Clone the model class and add extends function.
const classClone = withExtends(class extends (genericModel as AnyModelClass) {} as AnyModelClass as ConstructorOf<ModelType & Extension>);
// Add extension to the model class prototype.
Object.assign(classClone.prototype, extension);
return classClone;
}
}
) as AnyModelClass as ModelClass<ModelType, Shape, Identifier>;
} }

View file

@ -9,6 +9,11 @@ class Author extends s.model({
email: s.property.string(), email: s.property.string(),
createdAt: s.property.date(), createdAt: s.property.date(),
active: s.property.bool(), active: s.property.bool(),
}).extends({
extension(): string
{
return this.name;
}
}) })
{ {
active: boolean = true; active: boolean = true;
@ -157,3 +162,9 @@ it("save with modified submodels", () => {
], ],
}); });
}); });
it("test author extension", () => {
const author = new Author();
author.name = "test name";
expect(author.extension()).toStrictEqual("test name");
});

View file

@ -11,6 +11,7 @@
"incremental": true, "incremental": true,
"sourceMap": true, "sourceMap": true,
"noImplicitAny": true, "noImplicitAny": true,
"noImplicitThis": true,
"esModuleInterop": true, "esModuleInterop": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"resolveJsonModule": true, "resolveJsonModule": true,
@ -24,6 +25,6 @@
"lib": [ "lib": [
"ESNext", "ESNext",
"DOM" "DOM"
], ]
} }
} }