1 | // Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
|
2 | // Node module: @loopback/repository
|
3 | // This file is licensed under the MIT License.
|
4 | // License text available at https://opensource.org/licenses/MIT
|
5 |
|
6 | import * as assert from 'assert';
|
7 | import {DataObject, PrototypeOf} from './common-types';
|
8 | import {model} from './decorators';
|
9 | import {Model, ModelDefinition} from './model';
|
10 |
|
11 | /**
|
12 | * Create (define) a new model class with the given name and definition.
|
13 | *
|
14 | * @remarks
|
15 | *
|
16 | * ```ts
|
17 | * const Product = defineModelClass(Entity, new ModelDefinition('Product'));
|
18 | * ```
|
19 | *
|
20 | * To enable type safety, you should describe properties of your model:
|
21 | *
|
22 | * ```ts
|
23 | * const Product = defineModelClass<
|
24 | * typeof Entity,
|
25 | * {id: number, name: string}
|
26 | * >(Entity, new ModelDefinition('Product'));
|
27 | * ```
|
28 | *
|
29 | * If your model allows arbitrary (free-form) properties, then add `AnyObject`
|
30 | * to the type describing model properties.
|
31 | *
|
32 | * ```ts
|
33 | * const Product = defineModelClass<
|
34 | * typeof Entity,
|
35 | * AnyObject & {id: number},
|
36 | * >(Entity, new ModelDefinition('Product'));
|
37 | * ```
|
38 | *
|
39 | * @param base The base model to extend, typically Model or Entity.
|
40 | * You can also use your own base class, e.g. `User`.
|
41 | * @param definition Definition of the model to create.
|
42 | * @typeParam BaseCtor Constructor type of the base class,
|
43 | * e.g `typeof Model` or `typeof Entity`
|
44 | * @typeParam Props Interface describing model properties,
|
45 | * e.g. `{title: string}` or `AnyObject & {id: number}`.
|
46 | */
|
47 | export function defineModelClass<
|
48 | BaseCtor extends typeof Model,
|
49 | Props extends object = {},
|
50 | >(
|
51 | base: BaseCtor /* Model or Entity */,
|
52 | definition: ModelDefinition,
|
53 | ): DynamicModelCtor<BaseCtor, Props> {
|
54 | const modelName = definition.name;
|
55 | const defineNamedModelClass = new Function(
|
56 | base.name,
|
57 | `return class ${modelName} extends ${base.name} {}`,
|
58 | );
|
59 | const modelClass = defineNamedModelClass(base) as DynamicModelCtor<
|
60 | BaseCtor,
|
61 | Props
|
62 | >;
|
63 | assert.equal(modelClass.name, modelName);
|
64 |
|
65 | // Apply `@model(definition)` to the generated class
|
66 | model(definition)(modelClass);
|
67 | return modelClass;
|
68 | }
|
69 |
|
70 | /**
|
71 | * A type describing a model class created via `defineModelClass`.
|
72 | *
|
73 | * Assuming template arguments `BaseCtor` and `Props`, this type describes
|
74 | * a class constructor with the following properties:
|
75 | * - a constructor function accepting `DataObject<Props>` as the only argument,
|
76 | * this argument is optional
|
77 | * - all static fields (properties, methods) from `BaseCtor` are inherited and
|
78 | * available as static fields on the dynamic class
|
79 | * - all prototype fields from `BaseCtor` prototype are inherited and available
|
80 | * as prototype fields on the dynamic class
|
81 | */
|
82 | export type DynamicModelCtor<
|
83 | BaseCtor extends typeof Model,
|
84 | Props extends object,
|
85 | > = {
|
86 | /** Model constructor accepting partial model data. */
|
87 | new (
|
88 | data?: DataObject<PrototypeOf<BaseCtor> & Props>,
|
89 | ): PrototypeOf<BaseCtor> & Props;
|
90 | } & BaseCtor;
|