Skip to content

Mixin language support (with toy implementation) #2919

Description

@dbarbeau

Hello TypeScripters.

I might be opening a can of worms which might bring an age of darkness upon us (but see PS). Anyway, I've done an attempt to add language support for mixins.

For the user, it is similar to the extends or implements keywords, and thus goes by mixes. The semantic intent of the keyword is that a Mixture is NOT a derived class of its Mixins, it just copies their static and dynamic properties.

Several mixins can be supplied, the order determines which property will finally be mixed (newer definitions shadow previous definitions).

What this does is roughly equivalent to http://www.typescriptlang.org/Handbook#mixins, except there is no need to copy attributes manually, so mixins become more manageable - hopefully.

class Base {
    say(hello:string) {
        console.log(hello);
    }
}

class Mixin1 {
    something : string = "Couac couac";

    fn1() {
            console.log(this.something);
    }
}


class Mixin2 {      
    something : string = "shadowing you, little duck!";  

    fn2() {
            console.log("The dragon is", this.something);
    }
}

/* Only mixes one mixin */
class SimpleMixture mixes Mixin1 {

}

/* Mixes several mixins, property definitions in newer mixins (from left
to right) shadow those from previous mixins AND those in the mixture
class itself, including inherited ones. */
class MultiMixture extends Base mixes Mixin1, Mixin2 {

}


var sm1 = new SimpleMixture();
sm1.fn1();

var sm2 = new MultiMixture();
sm2.fn1();
sm2.fn2();

console.log( sm1 instanceof Base );
console.log( sm1 instanceof Mixin1 );
console.log( sm2 instanceof Base );
console.log( sm2 instanceof Mixin2 );

Which emits

var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var __applyMixins = this.__applyMixins || function (mixtureTarget) {
    for(var a=1; a<arguments.length; ++a) {
        var mixin = arguments[a];
        Object.getOwnPropertyNames(mixin.prototype).forEach( function(name) {
            mixtureTarget.prototype[name] = mixin.prototype[name];
        })
    }; 
};
var Base = (function () {
    function Base() {
    }
    Base.prototype.say = function (hello) {
        console.log(hello);
    };
    return Base;
})();
var Mixin1 = (function () {
    function Mixin1() {
        this.something = "Couac couac";
    }
    Mixin1.prototype.fn1 = function () {
        console.log(this.something);
    };
    return Mixin1;
})();
var Mixin2 = (function () {
    function Mixin2() {
        this.something = "shadowing you, little duck!";
    }
    Mixin2.prototype.fn2 = function () {
        console.log("The dragon is", this.something);
    };
    return Mixin2;
})();
/* Only mixes one mixin */
var SimpleMixture = (function () {
    function SimpleMixture() {
        Mixin1.apply(this) ;
    }
    __applyMixins(/*mixtureTarget*/ SimpleMixture, Mixin1);
    return SimpleMixture;
})();
/* Mixes several mixins, property definitions in newer mixins (from left
to right) shadow those from previous mixins AND those in the mixture
class itself, including inherited ones. */
var MultiMixture = (function (_super) {
    __extends(MultiMixture, _super);
    function MultiMixture() {
        _super.apply(this, arguments);
        Mixin1.apply(this) ;
        Mixin2.apply(this) ;
    }
    __applyMixins(/*mixtureTarget*/ MultiMixture, Mixin1, Mixin2);
    return MultiMixture;
})(Base);
var sm1 = new SimpleMixture();
sm1.fn1();
var sm2 = new MultiMixture();
sm2.fn1();
sm2.fn2();
console.log(sm1 instanceof Base);
console.log(sm1 instanceof Mixin1);
console.log(sm2 instanceof Base);
console.log(sm2 instanceof Mixin2);

and outputs

>>> Couac couac
>>> shadowing you, little duck!
>>> The dragon is shadowing you, little duck!
>>> false
>>> false
>>> true
>>> false

At this point it seems to work with target ES<6 with quite some limitations (the user can't pass arguments to the mixin constructors). Type checking seems to be working (although I could not make Eclipse or Emacs behave with the new keyword). But be warned Very early experimental preview code from a bad JS programmer who knew nothing about TSC's internals three days ago and who was in a rush. Be warned.

I did this to overcome a maintainability issue with my code and rushed this implementation. It was also an exercise to learn more about TSC. I'm also aware that it might not be a great idea to add keywords to a language each time one gets into trouble, but if others see interest in it or you have suggestions, the branch is here https://github.com/dbarbeau/TypeScript/tree/mixes. Just don't expect this implementation to be stable/complete/anything. Actually, if it can just spark some interest/discussion on how to make mixins more useable, then I'm ok with that.

Daniel
PS: Don't get mad at me :)
PS: I'm now testing this with more "serious code", I will quickly see if it is indeed practical.

Activity

  1. DanielRosenwasser commented on Apr 27, 2015

    @DanielRosenwasser
    Member

    Hey Daniel (@dbarbeau), thanks for the heads up on this interesting work! While we're mainly focusing on ES6 work right now, it's interesting to see this what you've done here. Hopefully we'll get some time to sit down, look at some of the changes and, like you said, get an idea of what might be involved if we wanted something like this.

  2. whitneyit commented on Apr 27, 2015

    @whitneyit
    Contributor

    This looks very similar to the conversation around traits in #311

  3. dbarbeau commented on Apr 27, 2015

    @dbarbeau
    Author

    Thanks for the feedback and for pointing out discussion 311. We're going to the same place but they seem to be taking a different way. In both cases there is a new keyword, trait vs mixes (one is a noun, the other a verb). Their proposal is based on a new structure type. My version extends the class declaration. I think my proposal is somewhat more lightweight (and, coming from C++, traits refer to trait classes which are something else) but they do have a lot more background (both academic and with a variety of use cases I didn't even imagine). The comparison is interesting!

  4. whitneyit commented on Apr 27, 2015

    @whitneyit
    Contributor

    Yea I agree with the lightweight point. I believe that is the strength and
    weakness of mixins over traits. I'll hedge my bets that only one would get
    implemented if that.

    It's defiantly an interesting topic and in either case I hope that one gets
    implemented.

    Regards,
    James
    On 27 Apr 2015 7:19 pm, "dbarbeau" notifications@github.com wrote:

    Thanks for the feedback and for pointing out discussion 311. We're going
    to the same place but they seem to be taking a different way. In both cases
    there is a new keyword, trait vs mixes (one is a noun, the other a verb).
    Their proposal is based on a new structure type. My version extends the
    class declaration. I think my proposal is somewhat more lightweight (and,
    coming from C++, traits refer to trait classes which are something else)
    but they do have a lot more background (both academic and with a variety of
    use cases I didn't even imagine). The comparison is interesting!

    —
    Reply to this email directly or view it on GitHub
    #2919 (comment)
    .

  5. dbarbeau commented on Apr 27, 2015

    @dbarbeau
    Author

    The "design decisions" I made were mostly guided by the urge of having something running quickly - which might not be the best way to design a language.

    That's why, after contemplating Python's MRO, I went against it : I wouldn't get it right in such a short term. I then figured that conceptually I didn't need anything more than what is described in the handbook, just automate it. Mixins are just that, mixins, not inheritance, MRO would be overkill. Thus the "newer shadows older declaration" approach to conflict solving (conflict smashing would better suit). I couldn't decide if users should be warned about the smashing or not.

    That's also why I chose the "mixes" verb, it'd be easier to grep through TSC how "extends" is implemented if I did something similar.

    So a bunch of hacks nothing more. The important for me is to have something running. Whatever gets into TS in the end is not important as long as something gets in there to help us mixers! I can then port my code to whatever is chosen.

    Regards,
    Daniel

    PS: I finally got emacs-tss to behave! As easy as a symlink in the end :)

  6. danquirk commented on Apr 27, 2015

    @danquirk
    Member

    Cool stuff. For reference, some other issues related to this concept are #371, #727, #1256

  7. dbarbeau commented on Apr 27, 2015

    @dbarbeau
    Author

    Thanks Dan Quirk (@danquirk) . There is a lot more to mixins than I originally thought!

    For example, this proposal doesn't satisfy Ryan Cavanaugh (@RyanCavanaugh)'s "after-the-fact mixing" requirement (371), which I clearly had no clue about, I tend to overlook the interfacing to existing JS code.

    Issue 311 presents attribute renaming to avoid name conflicts, which allows to do fine mixing instead of brutal shadowing. In the case where the mixins come from one project, chances are that they won't shadow themselves unless it's by design. But when mixing mixins from different origins, attribute renaming can come in handy.

    Issue 729 seems to more easily satisfy the "after-the-fact mixing", since it is not in the class declaration but as a decorator function. The | operator is confusing to me because it reuses the union type syntax in a slightly different context. But I does resemble the bitwise | operator in some way. It supports attribute renaming. I think the decorator approach could be great if there was a syntax to also put it close the mixture declaration. In new code, one would put the mixins "inline" rather than after the fact

    @mymix // a-la Python, more in-context
    class Mixture {
    }

    Where mymix implements whatever mixing logic is needed. But this is already different from 729 in that mymix would be a high-order function taking an original type and returning a new type (which can shadow the original), not an instance (and from there, the | operator would become useless for mixins). Going down that road, there are no mixin classes but high-order decorators (which can mix classes if the user wants to). How all this is implemented and translates to JS is a mystery, specially in the init of the new class.

    Concerning 1256's Intersection Types (thankfully, the Bool alphabet is rather small) I'm trying to grasp the concept but haven't yet found a resource which doesn't satellize me immediately, so I can't comment right now on how this proposal is related to 1256. Sorry.

    Daniel

  8. dbarbeau commented on Apr 27, 2015

    @dbarbeau
    Author

    In this proposal, concerning my issue with calling mixins' constructors, I think I don't need them. The mixins' default constructors are always called with this (right after any super call). this (the mixture) has all the mixed in attributes available in its own constructor and can give values to them.

    Ah, I noticed that private mixed-in attribute remain inaccessible to the mixture. I don't think it's desirable : the mixture's state IS exactly the mixin's state (state==members), no?

  9. jbondc commented on Apr 28, 2015

    @jbondc
    Contributor

    Daniel (@dbarbeau) Nice hacking

    You might find this interesting:
    https://wiki.php.net/rfc/traits

    The Scala example at the bottom was also suggested (with mixin), some interesting history there.

  10. dbarbeau commented on Apr 29, 2015

    @dbarbeau
    Author

    Thanks Jon (@jbondc)! That is an interesting read, I hope to get some time to dive deeper into it.

    Reading all those links leads me to think that, alright my implementation is lightweight and solved an immediate issue for me (with a slight tweak in the mixin precedence : the mixture's implementation should take precedence over mixins' in my very own case, like they do in the PHP link), but really, it is too lightweight to be future proof.

    What may be emerging is a Type algebra, some sort of meta-programming (there's already type union, talk about type intersection...). If that metaprogramming could be taken to the level of a hypothetic Typescript-Type-DSL (that may just look like ES/JS/TS, and can be parsed like it but works on types, just avoid C++ template-style metaprog :) ), you could write things like this (just letting the thoughts machine run, not meant to be realistic):

    // Takes type arguments and returns a new type.
    // Implements the handbook's mixin algo. Could be part of the stdlib.
    function mixin<...Mixins> : <> { // this signature tells the compiler that we're using the type DSL.
        // fun with metaprogramming...
        // Return new type;
    }
    
    // For after-the-fact mixing, maybe?
    // Takes type arguments and returns a new type.
    // Implements the handbook's mixin algo. Could be part of the stdlib.
    function post_mixin<Mixture, ...Mixins> : <> {
        // fun with metaprogramming...
        // Return new type;
    }
    
    // Takes no type arguments and returns a new type
    function custom_static_mixin<> : <> {
        // The user chose to create a new type from a predefined set of classes.
        // Copy properties, rename these, modify flags if you wish because you have a DSL for that.
        // Return new type;
    }
    
    /*Since mixin returns a type and extends expects a type, this is valid:*/
    class MyClass extends mixin<M1,M2,M3>  {
    }
    
    /*Same here, name ambiguity can be resolved by taking the function which returns a type*/
    class MyOtherClass extends custom_static_mixin {
    } 
    
    /* After the fact? */
    class A {
    }
    A = post_mixin<A, M1, M2, M3>; // mixin and overwrite original type.

    In this hypothesis functions convey the idea that the user can use a traditional programming metaphor (e.g. imperative) to customize the output type, instead of a declarative way that may need to extend the language syntax and more lexer/parser effort.

    Daniel

  11. dead-claudia commented on Jun 17, 2015

    @dead-claudia

    I do feel that an even better idea would be permitting default interface implementations. Java had this very problem, and fixed it with default interface implementations in Java 8. I feel this could profit from the same addition. (I filed #3536 regarding this alternative)

  12. nippur72 commented on Jun 29, 2015

    @nippur72

    Can we replace the verb "mixes" with the word "with" (a la Dart):

    class A extends B with M1, M2 { 
    }
    
  13. dead-claudia commented on Jun 29, 2015

    @dead-claudia

    I agree that the with seems a little clearer and more descriptive of the intent. Also, it's a little easier to reserve, since with isn't a valid identifier already (and won't need new highlighting for a lot of IDEs).

  14. benliddicott commented on Jul 14, 2015

    @benliddicott

    Well while we are all at it, here is my take. Anything implemented by a class listed further to the left overrides anything implemented by a class further to the right.

    "use strict";
    /**
     * Mixins for multiple inheritance. 
     * Usage:
     * class Derived extends BaseOne implements BaseTwo, BaseThree{
     *      baseTwoFunction: (a:number)=>any;
     *      baseThreeProperty: string;
     * 
     *      // Apply any missing base class functions to the derived prototype
     *      private static _mixins = Mixins.Apply(Derived, [BaseTwo, BaseThree]);
     * }
     */
    module Mixins {
        export function Apply(derivedClass: any, baseClasses: any[]) {
            var ret: { baseClass: any, functions: { name: string; done: boolean; type: string; }[] }[];
            ret = [];
            baseClasses.forEach(baseClass=> {
                ret.push({ baseClass: baseClass, functions: [] });
                Object.getOwnPropertyNames(baseClass.prototype).forEach(name=> {
                    // ONLY if doesn't already exist.
                    // Is this a property descriptor on the base class prototype?
                    var basePD = Object.getOwnPropertyDescriptor(baseClass.prototype, name);
                    var derivedPD = Object.getOwnPropertyDescriptor(derivedClass.prototype, name);
                    if (basePD && !derivedPD) {
                        Object.defineProperty(derivedClass.prototype, name, basePD);
                        ret[ret.length - 1].functions.push({
                            name: name, done: true, type: (basePD.get && basePD.set ? "get set" : basePD.get ? "get" : basePD.set ? "set" : basePD.value ? "method" : "")
                        });
                    } else {
                        ret[ret.length - 1].functions.push({
                            name: name, done: false, type: (basePD.get && basePD.set ? "get set" : basePD.get ? "get" : basePD.set ? "set" : basePD.value ? "method" : "")
                        });
                    }
                });
            });
            return ret;
        }
    } 
    

    Further thoughts:

    It would be good for mixin-aware mixins to be able to be called in the super() call. I.e. they could provide a method mixin or init or something, to be called as BaseTwo.prototype.init.call(this). These would be called in right-to-left order.

    Note also that currently mixins cannot have protected or private members. See #3854 for a proposal on how to fix this.

  15. 22 remaining items

  16. jklmli commented on Feb 23, 2016

    @jklmli

    jods (@jods4)
    The example I gave was to illustrate a roadblock I ran into (actually earlier today 😛) with the approach you outlined earlier, which didn't have a straightforward way of representing self-types. Naively writing abstract class C extends A won't work because it'll mix in A again.

    You're correct in your analysis that this isn't an issue with self-types, and that a correct ordering of A, B, C will result. The MDN approach supports this.

  17. unional commented on Mar 9, 2016

    @unional
    Contributor

    @jiaweihli , How do you implement the MDN's approach in TypeScript? I'm interested to learn. Do you have an example?

  18. jklmli commented on Mar 11, 2016

    @jklmli

    Homa Wong (@unional) Here's what I'm using right now:

    const Dog =
      // type prerequisite: can only mixin to classes derived from Base,
      // but won't mixin Base again.
      (baseClass: { new(...args: Array<any>): Base }) => {
        return class extends baseClass {
          bark(): void { console.log('bark'); }
        };
      };
    
    const Duck =
      (baseClass: { new(...args: Array<any>): Base }) => {
        return class extends baseClass {
          quack(): void { console.log('quack'); }
        };
      };
    
    class BaseBase {
      constructor() {
        console.log('base base ctor');
      }
    
      ping() {
        console.log('pong');
      }
    }
    
    class Base extends BaseBase {
      constructor() {
        super();
        console.log('base ctor');
      }
    
      world = "world";
    
      hello() {
        console.log(`hello ${this.world}`);
      }
    }
    
    class Mutant extends Dog(Duck(Base)) {}
    
    let xavier = new Mutant();
    xavier.bark();
    // :KLUDGE: Type inferencing doesn't work for any mixins after the first.  
    // But the compiled code is correct.
    xavier['quack']();
    xavier.hello();
    xavier.ping();
  19. jklmli commented on Mar 11, 2016

    @jklmli

    Daniel Rosenwasser (@DanielRosenwasser) re: my code above ^
    It looks like dynamic anonymous classes don't have full type inferencing. If I manually declare:

    const Quacker = class extends Base {
      quack(): void { console.log('quack'); }
    };
    
    const BarkQuacker = class extends Quacker {
      bark(): void { console.log('bark'); }
    };
    
    (new BarkQuacker).quack();

    then things work as expected.

    Is there any chance of improving the above issue? If this is fixed, then traits can work with no additional effort.

  20. jods4 commented on Mar 12, 2016

    @jods4

    @jiaweihli The :KLUDGE: is a major drawback, you loose all typing for all but one mixin...

    The problem is that statically, Dog returns Base & { bark(): void } (more or less). That's why you loose any other type info.

    Dynamic type info is not possible (by definition) but the closest thing is generics.

    Unfortunately, when I try

    function Dog<T extends Base>(baseClass: { new(...args: Array<any>): T }) => {
        return class extends baseClass {
          bark(): void { console.log('bark'); }
        };
      };

    It does not compile because TS does not recognize baseClass as a class anymore.

    At its hearth, this is the same limitation that requires the supplementary interface in my code above.

  21. jklmli commented on Mar 12, 2016

    @jklmli

    jods (@jods4)

    Yeah, I didn't realize this issue until today when I tried to apply the approach to multiple mixins 😞.
    Luckily for me, I only need a single mixin for the problem I'm trying to solve (but I do need to avoid overwrites from repeated inherited types).

    The multiple mixins compose correctly from the compiled code, but it seems like some extra work is needed to fix the type inferencing in the compiler.

  22. dead-claudia commented on Mar 12, 2016

    @dead-claudia

    Edit: fix misplaced variable

    Wonder if this could be solved with rest types?

    interface MixinType1<T extends Object, ...U extends Object[]>
        extends T & MixinType<...U> {}
    
    type MixinType<T extends Object, ...U extends Object[]> = T & MixinType1<...U>
    
    function mixin<T extends Object, ...U extends Object[]>(host: MixinType<T, ...U>, ...objects: ...U)
    function mixin<
        T extends Object & {[key: string]: U},
        U,
        ...V extends Array<Object | {[key: string]: U>
    >(host: MixinType<T, ...V>, ...objects: ...V)
    
    function mixin(host: {[key: string]: any}, ...args: {[key: string]: any}[]) {
        for (const arg of args) {
            for (const key of Object.keys(arg)) {
                (<any> host)[key] = (<any> arg)[key]
            }
        }
    
        return host
    }
  23. falsandtru commented on Jun 11, 2016

    @falsandtru
    Contributor

    My polyfill:

    export function Mixin<T>(...mixins: Array<new (...args: any[]) => any>): new (...args: any[]) => T {
      return mixins.reduceRight((b, d) => __extends(d, b), class { });
    }
    
    function __extends(d: new () => any, b: new () => any): new () => any {
      const __ = class {
        constructor() {
          return d.apply(b.apply(this, arguments) || this, arguments);
        }
      };
      void Object.assign(__.prototype, d.prototype, b.prototype);
      for (const p in b) if (b.hasOwnProperty(p)) __[p] = b[p];
      for (const p in d) if (d.hasOwnProperty(p)) __[p] = d[p];
      return __;
    }
        it('2', () => {
          let cnt = 0;
          class A {
            constructor(n: number) {
              assert(++cnt === 1);
              assert(n === 0);
            }
            static a = 'A';
            ap = 'a';
            am() {
              return this.ap;
            }
          }
          class B {
            constructor(n: number) {
              assert(++cnt === 2);
              assert(n === 0);
            }
            static b = 'B';
            bp = 'b';
            bm() {
              return this.bp;
            }
          }
          interface AB extends B, A {
          }
          class X extends Mixin<AB>(B, A) {
            constructor(n: number) {
              super(n);
              assert(++cnt === 3);
              assert(n === 0);
            }
            static x = 'X';
            xp = 'x';
            xm() {
              return this.xp;
            }
          }
          const x = new X(0);
          assert(++cnt === 4);
          assert(x instanceof A === false);
          assert(x instanceof B === false);
          assert(x instanceof X);
          assert(X['a'] === 'A');
          assert(X['b'] === 'B');
          assert(X.x === 'X');
          assert(x.ap === 'a');
          assert(x.bp === 'b');
          assert(x.xp === 'x');
          assert(x.am() === 'a');
          assert(x.bm() === 'b');
          assert(x.xm() === 'x');
        });
  24. kitsonk commented on Jun 12, 2016

    @kitsonk
    Contributor

    falsandtru (@falsandtru) thanks for sharing. Of course a polyfill generally is a term to provide functionality for some standard that exists when not natively supported. I am unaware of any standard that currently exists in ECMAScript (partly why the TypeScript have been loathe to address it).

    Your solution, like a lot of other solutions, would benefit from the rest operator for generics, but even then there are still some problems with all of this. While not a problem, it is a significant amount of boilerplate to create the transitory interface AB. Also, there is currently no way to model in the types what your actual _extends does. While the particular example you give doesn't have any property conflicts, it quickly falls down when you do and your typing can quite easily not represent the actual code. For example:

    class A {
      a: string;
    }
    
    class B {
      a(): string {
        return 'a'
      }
    }
    
    interface AB extends B, A { } // Error cannot simultaneously extend
    
    type ABType = B & A; // Can't be used with Mixin, but is type `string & () => string`
    
    class C extends Mixin<AB>(A, B) {
        c: number;
    }

    This are similar problems we have run into with dojo-compose although we have been able to get a level of type inference working where in many use cases, the user doesn't have to do any boilerplate, thought our method resolution, like the __extends you provided cannot be modelled, so it requires the user to know this and provide an final interface that represents it accurately.

    This is why I hope, at some point, we are provided with a way to programatically modify the types, either through ambient decorators or more feature rich type operators.

  25. dead-claudia commented on Jun 17, 2016

    @dead-claudia

    Based on what I'm starting to see in ES developments, I highly suspect that mixins will end up as simple decorators, and if it ever becomes part of the language, it would just end up a new builtin function. You could do it this way in ES + decorators:

    const wm = new WeakMap()
    
    export function mixin(Cls) {
      class C extends Cls {
        static [Symbol.hasInstance](obj) {
          return Array.from(wm.get(C)).some(C => obj instanceof C)
        }
      }
    
      Object.defineProperty(C, "name",
        Object.getOwnPropertyDescriptor(Cls, "name"))
    
      wm.set(C, new Set())
      return C
    }
    
    export function mixes(...mixins) {
      return Cls => {
        for (const m of mixins) {
          if (!wm.has(m)) {
            throw new TypeError(`Cannot add non-mixin ${m.name} as a mixin`)
          }
    
          wm.get(m).add(Cls)
        }
    
        for (const proto of mixins.map(m => Object.getPrototypeOf(m.prototype)) {
          Object.getOwnPropertyNames(proto)
          .filter(prop => prop !== "constructor")
          .forEach(prop => {
            Object.defineProperty(Cls.prototype, prop,
              Object.getOwnPropertyDescriptor(proto, prop))
          })
        }
      }
    }

    If anything, the only way you can realistically get this kind of mixin in TypeScript is by supporting variadic type intersection (i.e. A & B & ...) somehow. And that requires variadic types first, which don't yet exist.

  26. shlomiassaf commented on Nov 30, 2016

    @shlomiassaf

    Kitson Kelly (@kitsonk), note that falsandtru (@falsandtru) 's solution will throw an error when the compilation target is ES6 and up.

    You can not treat a class as a function in ES6.

    Invoking a class constructor results in the following error:

    TypeError: Class constructor XXXXXX cannot be invoked without 'new'
    
  27. ahejlsberg commented on Jan 30, 2017

    @ahejlsberg
    Member

    Closing as mixin classes are now supported in #13743.

  28. hoegge commented on Jul 13, 2017

    @hoegge

    Couldn't TypesScript extend just implement the described "workaround" in https://www.typescriptlang.org/docs/handbook/mixins.html so that it does like that when you write:

    class DaughterClass extends MotherClass, FatherClass, MailManClass {
        // additional implementation + constructor implementation that calls all super constructors
       constructor(public value: any) {
           super<Motherclass>.constructor(value);
           super<FatherClass>.constructor(value);
           super<MailManClass>.constructor(value);
           doSomethingMore(value);
        }
    
       niceCollidingMethod(aParameter: any) {
            super<MotherClass>.niceCollidingMethod(aParameter);
            aCommandToExecuteInBetween(aParameter);
            super<FatherClass>.niceCollidingMethod(aParameter);
            return finalStuffToDo(aParameter);
    }

    and then a requirement to re-implement methods or members that collide (diamond problem) which then has to call the super methods in addition to additional actions. Not sure if the suggested super<ancestor>.function notation would make sense / be accepted

  29. locked and limited conversation to collaborators on Jun 18, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    DiscussionIssues which may not have code impact

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions