[英]Mixins in TypeScript
我正在玩 TypeScript,我有幾個功能性 mixins , Eventable
和Settable
,我想將它們混合到Model
類中(假設它類似於 Backbone.js 模型):
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);
上面的代碼工作正常,但如果我嘗試使用其中一種混合方法,如(new Model()).set('foo', 'bar')
則無法編譯。
我可以解決這個問題
interface
聲明Model
聲明中聲明虛擬get
/ set
/ on
/ trigger
方法有沒有繞過虛擬聲明的干凈方法?
這是使用interfaces
和static create()
方法處理 mixin 的一種方法。 接口支持多重繼承,這樣您就不必為 mixin 重新定義interfaces
,並且static create()
方法負責將Model()
的實例作為IModel
返回給您Model()
需要<any>
IModel
來抑制編譯器警告。)您需要在IModel
上復制Model
所有成員定義,這很糟糕,但這似乎是在當前版本的 TypeScript 中實現您想要的最干凈的方法。
編輯:我已經確定了一種稍微簡單的方法來支持 mixin,甚至創建了一個幫助類來定義它們。 詳細信息可以在這里找到。
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');
盡管仍然需要雙重類型聲明,但最簡潔的方法是將 mixin 定義為模塊:
module Mixin {
export function on(test) {
alert(test);
}
};
class TestMixin implements Mixin {
on: (test) => void;
};
var mixed = _.extend(new TestMixin(), Mixin); // Or manually copy properties
mixed.on("hi");
使用接口的另一種方法是用類來破解它(盡管由於多重繼承,您需要為 mixins 創建一個公共接口):
var _:any;
var __mixes_in = _.extend; // Lookup underscore.js' extend-metod. Simply copies properties from a to b
class asSettable {
getx(key:string) { // renamed because of token-clash in asEventAndSettable
return this[key];
}
setx(key:string, value) {
this[key] = value;
return this;
}
}
class asEventable {
_events: any;
on(name:string, callback) {
this._events = this._events || {};
this._events[name] = callback;
}
trigger(name:string) {
this._events[name].call(this);
}
}
class asEventAndSettable {
// Substitute these for real type definitions
on:any;
trigger:any;
getx: any;
setx: any;
}
class Model extends asEventAndSettable {
/// ...
}
var m = __mixes_in(new Model(), asEventable, asSettable);
// m now has all methods mixed in.
正如我對 Steven 的回答所評論的那樣,mixin 確實應該是一個 TypeScript 功能。
一種解決方案是不使用 typescript 類 system ,而只使用類型和接口的 systeme ,以及關鍵字“new”。
//the function that create class
function Class(construct : Function, proto : Object, ...mixins : Function[]) : Function {
//...
return function(){};
}
module Test {
//the type of A
export interface IA {
a(str1 : string) : void;
}
//the class A
//<new () => IA> === cast to an anonyme function constructor that create an object of type IA,
// the signature of the constructor is placed here, but refactoring should not work
//Class(<IA> { === cast an anonyme object with the signature of IA (for refactoring, but the rename IDE method not work )
export var A = <new () => IA> Class(
//the constructor with the same signature that the cast just above
function() { } ,
<IA> {
//!! the IDE does not check that the object implement all members of the interface, but create an error if an membre is not in the interface
a : function(str : string){}
}
);
//the type of B
export interface IB {
b() : void;
}
//the implementation of IB
export class B implements IB {
b() { }
}
//the type of C
export interface IC extends IA, IB{
c() : void;
mystring: string;
}
//the implementation of IC
export var C = <new (mystring : string) => IC> Class(
//public key word not work
function(mystring : string) {
//problem with 'this', doesn't reference an object of type IC, why??
//but google compiler replace self by this !!
var self = (<IC> this);
self.mystring = mystring;
} ,
<IC> {
c : function (){},
//override a , and call the inherited method
a: function (str: string) {
(<IA> A.prototype).a.call(null, 5);//problem with call and apply, signature of call and apply are static, but should be dynamic
//so, the 'Class' function must create an method for that
(<IA> this.$super(A)).a('');
}
},
//mixins
A, B
);
}
var c = new Test.C('');
c.a('');
c.b();
c.c();
c.d();//ok error !
幾年前,Typescript 內置了一種新方法,稱為“mixin 類”。 文檔中沒有很好地涵蓋它,但是他們確實有一個評論良好的示例來很好地描述該模式。 適用於您的情況,它可能類似於:
type Constructor = new (...args: any[]) => {}
function Settable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
_props: Record<string, any> = {};
get(key: string) {
return this._props[key];
}
set(key: string, value: any) {
this._props[key] = value;
return this;
}
}
}
function Eventable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
_events: Record<string, () => void> = {};
on(name: string, callback: () => void) {
this._events[name] = callback;
}
trigger(name: string) {
this._events[name].call(this);
}
}
}
class Model extends Settable(Eventable(Object)) {
constructor(properties = {}) {
super();
}
}
這可以讓您輸入您想要的類型,例如,您可以調用(new Model()).set('boo', 'bar')
並提供完整的輸入支持。 沒有虛擬聲明。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.