@loopback/repository
Version:
Define and implement a common set of interfaces for interacting with databases
91 lines (86 loc) • 2.97 kB
text/typescript
// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
// Node module: @loopback/repository
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
import * as assert from 'assert';
import {DataObject, PrototypeOf} from './common-types';
import {model} from './decorators';
import {Model, ModelDefinition} from './model';
/**
* Create (define) a new model class with the given name and definition.
*
* @remarks
*
* ```ts
* const Product = defineModelClass(Entity, new ModelDefinition('Product'));
* ```
*
* To enable type safety, you should describe properties of your model:
*
* ```ts
* const Product = defineModelClass<
* typeof Entity,
* {id: number, name: string}
* >(Entity, new ModelDefinition('Product'));
* ```
*
* If your model allows arbitrary (free-form) properties, then add `AnyObject`
* to the type describing model properties.
*
* ```ts
* const Product = defineModelClass<
* typeof Entity,
* AnyObject & {id: number},
* >(Entity, new ModelDefinition('Product'));
* ```
*
* @param base The base model to extend, typically Model or Entity.
* You can also use your own base class, e.g. `User`.
* @param definition Definition of the model to create.
* @typeParam BaseCtor Constructor type of the base class,
* e.g `typeof Model` or `typeof Entity`
* @typeParam Props Interface describing model properties,
* e.g. `{title: string}` or `AnyObject & {id: number}`.
*/
export function defineModelClass<
BaseCtor extends typeof Model,
Props extends object = {},
>(
base: BaseCtor /* Model or Entity */,
definition: ModelDefinition,
): DynamicModelCtor<BaseCtor, Props> {
const modelName = definition.name;
const defineNamedModelClass = new Function(
base.name,
`return class ${modelName} extends ${base.name} {}`,
);
const modelClass = defineNamedModelClass(base) as DynamicModelCtor<
BaseCtor,
Props
>;
assert.equal(modelClass.name, modelName);
// Apply `@model(definition)` to the generated class
model(definition)(modelClass);
return modelClass;
}
/**
* A type describing a model class created via `defineModelClass`.
*
* Assuming template arguments `BaseCtor` and `Props`, this type describes
* a class constructor with the following properties:
* - a constructor function accepting `DataObject<Props>` as the only argument,
* this argument is optional
* - all static fields (properties, methods) from `BaseCtor` are inherited and
* available as static fields on the dynamic class
* - all prototype fields from `BaseCtor` prototype are inherited and available
* as prototype fields on the dynamic class
*/
export type DynamicModelCtor<
BaseCtor extends typeof Model,
Props extends object,
> = {
/** Model constructor accepting partial model data. */
new (
data?: DataObject<PrototypeOf<BaseCtor> & Props>,
): PrototypeOf<BaseCtor> & Props;
} & BaseCtor;