Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mixin #56

Open
basarat opened this issue Jan 20, 2016 · 3 comments
Open

Mixin #56

basarat opened this issue Jan 20, 2016 · 3 comments

Comments

@basarat
Copy link
Owner

basarat commented Jan 20, 2016

Here is a good solution : microsoft/TypeScript#6502 (comment) that uses Object.assign

microsoft/TypeScript#2919 (comment)

function Mixin<T>(...mixins) : new() => T {
    class X {
        constructor() {
            mixins[0].call(this);
        }
    }
    Object.assign(X.prototype, ...mixins.map(m => m.prototype));
    return <any>X;
}

abstract class Dog {
    bark() { console.log('bark!'); }
}

abstract class Duck {
    quack() { console.log('quack!'); }
}

class Base {
    constructor() {
        console.log('base ctor');
    }

    world = "world";

    hello() {
        console.log(`hello ${this.world}`);
    }
}

interface DoggyDuck extends Base, Dog, Duck { }

class Mutant extends Mixin<DoggyDuck>(Base, Dog, Duck) {

}

let xavier = new Mutant();
xavier.bark();
xavier.quack();
xavier.hello();
@jods4
Copy link

jods4 commented Jan 21, 2016

Please keep in mind this was a quick proof of concept made for the discussion in microsoft/TypeScript#6502. There are several aspects that are debatable, in particular the signature and implementation of the Mixin helper function. I tried to go for the most straightforward demo possible.

A first key improvement would be making the base class optional.

Please also pay attention to this comment where I hint at some languages improvements that may make this even safer and better.

The gist of the comment is that I couldn't find a solution other than introducing a dummy, empty DoggyDuck interface for each mixins combination.

If the TS compiler could see T & U as a "class or interface" when T or U is a "class or interface", then it would be possible to do this, which does not currently compile:

function Mixin<A,B,C>(a: () => A, b: () => B, c: () => C): () => A & B & C { ... }

// Then without an additional interface:
class X extends Mixin(Base, Dog, Duck) { ... }

Note that I did not think this through, so maybe it's non-sense ;)
Big drawback is that you would need one overload for each arity... I don't think that the variadic proposal for 1.9 will help here because there's no way to transform <...T> into T[0] & T [1] & T[2].

@basarat
Copy link
Owner Author

basarat commented Jan 21, 2016

@jods4 thanks a lot. I'll wait for consensus 🌹

@basarat
Copy link
Owner Author

basarat commented Jan 23, 2017

This is the placeholder we need to update : https://github.com/basarat/typescript-book/blob/0dbdc38c0df33096797beffc7ce4b9d86ce239e2/docs/tips/mixins.md

There is new work on TypeScript master that makes the mixin pattern easier : microsoft/TypeScript#13604 🌹

The new pattern is also covered in a blog post : https://blogs.msdn.microsoft.com/typescript/2017/02/02/announcing-typescript-2-2-rc/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants