Page Contents

It is a commonly used JavaScript/TypeScript strategy to extend a class with new properties and methods.

A good approach to apply mixins is defining them as sub-class factories. Then declare the new mixed class as:

class MixedClass extends MixinFoo(MixinBar(BaseClass)) {}

Check the article, “Real” Mixins with JavaScript Classes, to learn more about it.

Define a Mixin

By defining a mixin, you create a mixin function that takes in a base class, and returns a new class extending the base class with new properties and methods mixed to it.

For example, you have a simple controller which only has a greeter function prints out ‘hi!’:

src/controllers/using-mixin.controller.ts

class SimpleController {
  constructor() {}
  greet() {
    console.log('hi!');
  }
}

Now let’s add two mixins to it:

  1. A time stamp mixin that adds a property createdAt to a record when a controller instance is created.

  2. A logger mixin to provide logging tools.

Define mixin TimeStampMixin:

src/mixins/time-stamp.mixin.ts

import {MixinTarget} from '@loopback/core';
import {Class} from '@loopback/repository';

export function TimeStampMixin<T extends MixinTarget<object>>(baseClass: T) {
  return class extends baseClass {
    // add a new property `createdAt`
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    public createdAt: Date;
    constructor(...args: any[]) {
      super(args);
      this.createdAt = new Date();
    }
    printTimeStamp() {
      console.log('Instance created at: ' + this.createdAt);
    }
  };
}

And define mixin LoggerMixin:

src/mixins/logger.mixin.ts

import {MixinTarget} from '@loopback/core';
import {Class} from '@loopback/repository';

function LoggerMixin<T extends MixinTarget<object>>(baseClass: T) {
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore
  return class extends baseClass {
    // add a new method `log()`
    log(str: string) {
      console.log('Prints out a string: ' + str);
    }
  };
}

Now you can extend SimpleController with the two mixins:

src/controllers/using-mixin.controller.ts

import {TimeStampMixin} from '../mixins/time-stamp.mixin.ts';
import {LoggerMixin} from '../mixins/logger.mixin.ts';

class SimpleController {
  constructor() {}
  greet() {
    console.log('hi!');
  }
}

class AdvancedController extends LoggerMixin(
  TimeStampMixin(SimpleController),
) {}

// verify new method and property are added to `AdvancedController`:
let aControllerInst = new AdvancedController();
aControllerInst.printTimeStamp();
// print out: Instance created at: Tue Oct 17 2017 22:28:49 GMT-0400 (EDT)
aControllerInst.logger('hello world!');
// print out: Prints out a string: hello world!

References

Here are some articles explaining ES2015 and TypeScript mixins in more details: