Mixins a macchina

voti
6

Sto giocando intorno con dattiloscritto, e ho ottenuto un paio di mixins funzionali , Eventablee Settableche mi piacerebbe mixin ad una Modelclasse di (finta è qualcosa come un modello 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);

Il codice di cui sopra funziona bene, ma non sarebbe la compilazione se ho cercato di utilizzare uno dei metodi misti-in come (new Model()).set('foo', 'bar').

Posso lavorare intorno a questo

  1. l'aggiunta di interfacedichiarazioni per le mixins
  2. dichiarando fittizie get/ set/ on/ triggermetodi nella Modeldichiarazione

C'è un modo pulito intorno alle dichiarazioni fittizie?

È pubblicato 04/10/2012 alle 04:20
fonte dall'utente
In altre lingue...                            


3 risposte

voti
12

Ecco un modo per avvicinarsi mixins utilizzando interfacese un static create()metodo. Interfacce supporta l'ereditarietà multipla in modo che ti impedisce di dover ridefinire la interfacesper i vostri mixins ed il static create()metodo prende cura di dare indietro un'istanza di Model()come IModel(il <any>cast è necessario per sopprimere un avviso del compilatore.) Avrete bisogno di duplicare tutta le definizioni dei membri per Modelil IModelquale succhia ma sembra il modo più pulito per ottenere quello che vuoi nella versione attuale del dattiloscritto.

EDIT: ho identificato un approccio leggermente più semplice da mixins di sostegno e hanno addirittura creato una classe di supporto per la loro definizione. Dettagli possono essere trovati qui .

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');
Risposto il 04/10/2012 a 06:46
fonte dall'utente

voti
3

Il modo più pulito per farlo, ma decorate richiede ancora dichiarazioni doppie tipo, è quello di definire il mixin come modulo:

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");

Un'alternativa all'utilizzo di interfacce è quello di incidere con le classi (anche se a causa di multipli eredità, è necessario creare una comune interfaccia per i mixin):

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.

Come ho commentato la risposta di Steven, mixins in realtà dovrebbe essere una caratteristica dattiloscritto.

Risposto il 04/10/2012 a 07:04
fonte dall'utente

voti
1

Una soluzione è quella di non utilizzare il sistema di classe dattiloscritto, ma solo lo Systeme di tipi e interfacce, oltre alla parola 'nuovo'.

    //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 !
Risposto il 23/01/2013 a 00:34
fonte dall'utente

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more