web-dev-qa-db-ja.com

タイプスクリプトの多重継承

TypeScriptで多重継承が必要です。論理的には、多くの機能を階層に配置するのは良くありません。 1つの基本クラスといくつかの階層ブランチがあります。しかし、いくつかのメインロジックを別々のクラスに配置するためにミキシングを使用する必要があります。すべてのブランチで使用されるわけではないからです。

コード例の更新:

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

実際には、多重継承を実装する必要があるのはまれな状況であり、javascriptでこのような問題が発生することはほとんどありません。ただし、すべてのアイテムは、ニーズに応じて異なる機能を持つことができます。

いくつかのミックスインを持つことができるさまざまなアイテムがあることを想像してください。すべてをベースに入れるのは賢明ですか?そして、この問題に対するあなたのアプローチは何ですか?

13
Vayrex

現在、TypeScriptには、多重継承またはミックスインを表現するための特別な構文はありませんが、公式の回避策は here にあります。

その戦略は、基本的に、メインクラスimplementに必要なクラスを許可し、それらのインターフェイスに簡単なダミー実装を記述し、次に特別な機能を持たせることです。クラスを読み取り、メインクラスのプロパティを上書きします。

TypeScriptをmixin/traitsで改善するための提案がいくつかあります。 #2919 および #311

12
Alex

@ kjetil-klaussenは正しい、TypeScript 2.2は ECMAscript 2017 mixinパターンのサポートを追加しました

完全なコードサンプルを次に示します。

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

この出力:

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

TypeScript 2.2が追加されました mix-insのサポート

4
Kjetil Klaussen