43

I'm need of multiple inheritance in typescript. Logically it is not good to put a lot of functionality to hierarchy. I have one base class and number of hierarchy branches. But I need somehow to use mixings to put some main logic in separate classes, cuz it is not used in every branch.

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

In reality it is rare situation when you need to implement multiple inheritance, and it is much rarely when you can have such an issue in javascript. But every item can have different functionality depending on needs.

Just imagine that there can be different items which can have number of mixins. Is it wise to put everything into base ? And what is your approach to this issue ?

Vayrex
  • 1,417
  • 1
  • 11
  • 13
  • Imo you should use DI for this.. – Dieterg Nov 25 '16 at 15:33
  • 2
    @LenilsondeCastro Although the idea of multiple inheritance often is dismissed in OOP, it's not really any violation of any principle, it's just that it's not trivial to implement and that we are taught that it's just a stupid idea anyway. In reality, all entities can be categorized in many different unrelated abstractions, and I think it would be neat to have first-class support for that kind of structural organization in a language. – Alex Nov 25 '16 at 15:47
  • @Dieterg Sorry I'm not sure how DI can help in such situation, but code example is updated. – Vayrex Dec 10 '16 at 19:14
  • @Alex I was having situation when multiple inheritance was good solution for small python script. And I was not using it in javascript before. But in my case I have a lot of different childs, which need to have separate functionality not only in rendering, but in subscribtions to event emitters and a lot of other things. I was searching for other solutions but mixins seems to be more logical here. – Vayrex Dec 10 '16 at 19:21
  • I hate Microsoft for not adding this, I truly do; I have a lot of code duplication thanks to not having a straight forward way of doing this. – Ivan Castellanos Feb 17 '19 at 00:35
  • Answered here https://stackoverflow.com/a/60944648/5172890 , which supports decorator inheritance as well. – kenberkeley Nov 17 '20 at 05:07

2 Answers2

32

TypeScript 2.2 added support for the ECMAscript 2017 mixin pattern

Here is a full code sample:

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

This outputs:

foo from C
foo from Mixin1
foo from Mixin2
foo from S
halfer
  • 19,824
  • 17
  • 99
  • 186
Simon Epskamp
  • 8,813
  • 3
  • 53
  • 58
  • Got an error `Property 'foo' does not exist on type '{}'.ts` at line: `if (super.foo) super.foo();` (TypeScript v4.8.4). – Ibrahim.H Oct 06 '22 at 18:55
18

Currently, TypeScript does not have any special syntax to express multiple inheritance or mixins, but the official workaround can be found here.

That strategy is basically that you let your main class implement the classes that you want, write simple dummy implementation for their interfaces, and then have a special function that reads your classes and overwrites the properties in your main class.

There are some suggestions to make TypeScript better at mixins/traits, eg. #2919 and #311.

Alex
  • 14,104
  • 11
  • 54
  • 77
  • Thank you. But I came here after reading of that posts. I have done update of code example and now main question is changed. – Vayrex Dec 10 '16 at 19:12
  • 2
    I despite Microsoft for not adding this, I truly do; I have a lot of code duplication thanks to not having a straight forward way of doing this. – Ivan Castellanos Feb 17 '19 at 00:37