Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mixins in TypeScript

I'm playing around with TypeScript, and I've got a couple functional mixins, Eventable and Settable, that I'd like to mixin to a Model class (pretend it's something like a Backbone.js model):

function asSettable() {
  this.get = function(key: string) {
    return this[key];
  };
  this.set = function(key: string, value) {
    this[key] = value;
    return this;
  };
}

function asEventable() {
  this.on = function(name: string, callback) {
    this._events = this._events || {};
    this._events[name] = callback;
  };
  this.trigger = function(name: string) {
    this._events[name].call(this);
  }
}

class Model {
  constructor (properties = {}) {
  };
}

asSettable.call(Model.prototype);
asEventable.call(Model.prototype);

The code above works fine, but would not compile if I tried to use one of the mixed-in methods like (new Model()).set('foo', 'bar').

I can work around this by

  1. adding interface declarations for the mixins
  2. declaring dummy get/set/on/trigger methods in the Model declaration

Is there a clean way around the dummy declarations?

like image 662
aaronstacy Avatar asked Oct 04 '12 03:10

aaronstacy


1 Answers

Here's one way to approach mixins using interfaces and a static create() method. Interfaces support multiple inheritance so that prevents you from having to redefine the interfaces for your mixins and the static create() method takes care of giving you back an instance of Model() as an IModel (the <any> cast is needed to supress a compiler warning.) You'll need to duplicate all of your member definitions for Model on IModel which sucks but it seems like the cleanest way to achieve what you want in the current version of TypeScript.

edit: I've identified a slightly simpler approach to supporting mixins and have even created a helper class for defining them. Details can be found over here.

function asSettable() {
  this.get = function(key: string) {
    return this[key];
  };
  this.set = function(key: string, value) {
    this[key] = value;
    return this;
  };
}

function asEventable() {
  this.on = function(name: string, callback) {
    this._events = this._events || {};
    this._events[name] = callback;
  };
  this.trigger = function(name: string) {
    this._events[name].call(this);
  }
}

class Model {
  constructor (properties = {}) {
  };

  static create(): IModel {
      return <any>new Model();
  }
}

asSettable.call(Model.prototype);
asEventable.call(Model.prototype);

interface ISettable {
    get(key: string);
    set(key: string, value);
}

interface IEvents {
    on(name: string, callback);
    trigger(name: string);
}

interface IModel extends ISettable, IEvents {
}


var x = Model.create();
x.set('foo', 'bar');
like image 167
Steven Ickman Avatar answered Sep 25 '22 06:09

Steven Ickman