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
interface
declarations for the mixinsget
/set
/on
/trigger
methods in the Model
declarationIs there a clean way around the dummy declarations?
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');
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With