web-dev-qa-db-fra.com

Héritage multiple dactylographié

J'ai besoin d'un héritage multiple dans TypeScript. Logiquement, il n'est pas bon de mettre beaucoup de fonctionnalités dans la hiérarchie. J'ai une classe de base et un certain nombre de branches de hiérarchie. Mais j'ai besoin d'une manière ou d'une autre d'utiliser des mélanges pour mettre une logique principale dans des classes distinctes, car elle n'est pas utilisée dans toutes les branches.

MISE À JOUR du code exmple:

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 réalité, il est rare que vous ayez besoin d'implémenter plusieurs héritages, et c'est très rarement lorsque vous pouvez avoir un tel problème en javascript. Mais chaque élément peut avoir des fonctionnalités différentes selon les besoins.

Imaginez simplement qu'il peut y avoir différents éléments qui peuvent avoir un certain nombre de mixins. Est-il sage de tout mettre en base? Et quelle est votre approche de ce problème?

13
Vayrex

Actuellement, TypeScript n'a pas de syntaxe spéciale pour exprimer l'héritage multiple ou les mixins, mais la solution officielle peut être trouvée ici .

Cette stratégie consiste essentiellement à laisser votre classe principale implémenter les classes que vous souhaitez, écrire une implémentation factice simple pour leurs interfaces, puis avoir une fonction spéciale qui lit vos classes et écrase les propriétés de votre classe principale.

Il y a quelques suggestions pour améliorer TypeScript au niveau des mixins/traits, par exemple. # 2919 et # 311 .

12
Alex

@ kjetil-klaussen est correct, TypeScript 2.2 a ajouté la prise en charge du modèle de mixage ECMAscript 2017

Voici un exemple de code complet:

// 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();

Cela produit:

foo from C
foo from Mixin1
foo from Mixin2
foo from S
8
Simon Epskamp

TypeScript 2.2 ajouté prise en charge des mix-ins .

4
Kjetil Klaussen