Herencia múltiple en Typescript
Necesito herencia múltiple en TypeScript. Lógicamente no es bueno poner mucha funcionalidad en la jerarquía. Tengo una clase base y varias ramas de la jerarquía. Pero de alguna manera necesito usar mezclas para poner algo de la lógica principal en clases separadas, porque no se utiliza en cada rama.
ACTUALIZACIÓN del ejemplo de código:
function Mixin = function(mixins:any[]){ // mixin decorator
return function(target){
mixins.forEach((mixin) => {
add every function from mixing to target prototype,
if functions with same name does not exists there,
so we are able to do calls to as example different render() functions
Will show it in OpenableItem
});
}
}
function element = function(){
return function(target){
target.prototype.element = function(){
return this.$el;
}
}
}
--------------------------------------------------------------------------------------
@element // every item will have this function as Root as Openable
class BaseItem{
y() => number; // we need to get y position of every item
}
OpenableMixin{
render() => render arrow only
open(){} => change arrow position and cause menu to fire change event
close(){} => change arrow position and cause menu to fire change event
}
class ItemsMixin extends OpenableMixing{// for now if item have childs it must be openable,
// but Responsive has only tasks and must be openable too
addItem(item: BaseItem) // need to add generics
removeItem(item: BaseItem)
}
--------------------------------------------------------------------------------------
@Mixin([ItemsMixin, ActivitiesMixin]) // it can have items and activities
class OpenableItem extends BaseItem implement ItemsMixin, ActivitiesMixin { // as in typescript docs
render(){
// call rendering from BaseItem class
super.render();
// call rendering from OpenableMixing
OpenableMixin.prototype.render.call(this);
// do some separate rendering for OpenableItem only
this.$el.append('line');
}
}
@Mixin([ItemsMixin]) // it can have items
class RootItem extends BaseItem implement ItemsMixin{ // and subitems functionality is only for Root class
subitems: Array<BaseItem> // need to add generics to be able to put here different item types
}
--------------------------------------------------------------------------------------
@element
class Menu{
items: Array<Item>
}
@element
class Timeline{
menu: Menu
listAllelement() => {
console.log(this.element());
console.log(this.menu.element());
this.menu.items.forEach((item) => {
console.log(item.element());
if(item.hasChilds()){ // really it must be (item instanceof RootItem || item instanceof OpenableItem)
item.items.forEach((subitem) => { // really we need some recursion here
console.log(subitem.element());
})
}
})
}
}
En realidad, es una situación rara cuando necesitas implementar herencia múltiple, y es mucho más raro cuando puedes tener tal problema en JavaScript. Pero cada elemento puede tener funcionalidades diferentes dependiendo de las necesidades.
Solo imagina que pueden existir diferentes elementos que pueden tener varios mixins. ¿Es prudente poner todo en la base? ¿Y cuál es tu enfoque para este problema?
Respuesta #1
TypeScript 2.2 añadió soporte para el patrón mixin de ECMAscript 2017
Aquí hay un ejemplo completo de código:
// http://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/
// https://github.com/Microsoft/TypeScript/wiki/What's-new-in-TypeScript#support-for-mix-in-classes
type Constructor<T> = new (...args: any[]) => T;
class S {
foo() {
console.log('foo from S');
}
}
// Here the possible SuperClass is set to {} (Object)
function Mixin1<T extends Constructor<{}>>(SuperClass: T) {
return class extends SuperClass {
foo() {
console.log('foo from Mixin1');
if (super.foo) super.foo();
}
};
}
// Here the possible SuperClass (S) is specified
function Mixin2<T extends Constructor<S>>(SuperClass: T) {
return class extends SuperClass {
foo() {
console.log('foo from Mixin2');
super.foo();
}
};
}
class C extends Mixin1(Mixin2(S)) {
foo() {
console.log('foo from C');
super.foo();
}
}
new C().foo();
Esto produce:
foo from C
foo from Mixin1
foo from Mixin2
foo from S
Respuesta #2
Actualmente, TypeScript no tiene ninguna sintaxis especial para expresar herencia múltiple o mixins, pero la solución oficial se puede encontrar aquí.
Esa estrategia básicamente consiste en que dejas que tu clase principal implemente las clases que deseas, escribes una implementación simple simulada para sus interfaces, y luego tienes una función especial que lee tus clases y sobrescribe las propiedades en tu clase principal.
Hay algunas sugerencias para mejorar TypeScript en mixins/traits, por ejemplo, #2919 y #311.
