# Mixins development
Each user of the library can write their own mixins. The mixin should provide a mixin function, private and public interfaces. The mixin function is a function that accepts a parent class and returns a mixed class:
import { type Constructor, BaseModel } from '@vueent/mix-models';
export interface Random {
readonly rand: number;
}
export interface RandomPrivate extends Random {
setMaxRandom(max: number): void;
}
export function randomMixin<D extends object, C extends Constructor<D, BaseModel<D>>>(parent: C) {
return class extends parent implements RandomPrivate {
#maxRandom = 1;
get rand(): number {
return Math.floor(Math.random() * this.#maxRandom);
}
setMaxRandom(max: number): void {
this.#maxRandom = max;
}
hasMixin(mixin: Function): boolean {
return mixin === randomMixin || super.hasMixin(mixin); // useful for dynamic type checking and JS
}
};
}
export function mixRandom() {
return <D extends object, C extends Constructor<D, BaseModel<D>>>(parent: C) => randomMixin<D, C>(parent);
}
TIP
Due to TypeScript limitations mixins cannot use access modifiers, but private (starting with #
) variables can be used.
So, the mixin above provides a rand
getter and a protected setMaxRandom
method.
# Class generating function
A class generating function is a generic function that accepts a parent constructor, the mixin parameters and returns a new mixed class.
# Factory function
A factory function should return a function like a class generating function, but with bound parameters if needed. The mix
function uses its result (see below).
# Private interface
A private interface is required to add protected fields and methods to a final model class.
# Public interface
A public interface contains public fields and methods only. These fields and methods can be used by the user of the model instance.
# Constructor arguments
Of course, a mixin can override the constructor, but it must have arguments that are compatible with the BaseModel
constructor.
# Usage
import { type Base, BaseModel, mix, Options } from '@vueent/mix-models';
import { mixRandom, type Random, type RandomPrivate } from '@/model-mixin/random';
interface Data {
id: number;
value: number;
}
function makeInitialData(): Data {
return { id: 0, value: 0 };
}
class DataModel extends BaseModel<Data> {}
export interface Model extends DataModel, RandomPrivate {}
export class Model extends mix<Data, typeof DataModel>(DataModel, mixRandom()) {
constructor(initialData?: Data, react = true, ...options: Options[]) {
super('id', initialData ?? makeInitialData(), react, ...options);
this.setMaxRandom(256); // provided by RandomPrivate interface
}
}
export type ModelType = Base<Data> & Random;
export function create<ModelOptions extends Options>(initialData?: Data, react = true, ...options: ModelOptions[]) {
return new Model(initialData, react, ...options) as ModelType;
}
const instance = create();
instance.data.value = instance.rand;
console.log(instance.data.value); // outputs: <a random number between 0 and 256>