Skip to content

Commit

Permalink
fix: wrong this in init functions for Mixin(A, Mixin(B, C)) scenario
Browse files Browse the repository at this point in the history
  • Loading branch information
tannerntannern committed Apr 23, 2020
1 parent 45ccd21 commit 0ba1128
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 41 deletions.
21 changes: 16 additions & 5 deletions src/mixins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,17 +197,28 @@ function Mixin<
function Mixin(...constructors: Class[]) {
const prototypes = constructors.map(constructor => constructor.prototype);

// NOTE: we save the init function name here because MixedClass could be called with different settings than Mixin
const initFunctionName = settings.initFunction;
if (initFunctionName !== null) {
const initFunctions: Function[] = prototypes
.map(proto => proto[initFunctionName])
.filter(func => typeof func === 'function');

const combinedInitFunction = function(...args) {
for (let initFunction of initFunctions)
initFunction.apply(this, args);
};

const extraProto = { [initFunctionName]: combinedInitFunction };

prototypes.push(extraProto);
}

function MixedClass(...args) {
for (const constructor of constructors)
copyProps(this, new constructor(...args));

if (initFunctionName !== null)
for (const constructor of constructors)
if (typeof constructor.prototype[initFunctionName] === 'function')
constructor.prototype[initFunctionName].apply(this, args);
if (initFunctionName !== null && typeof this[initFunctionName] === 'function')
this[initFunctionName].apply(this, args);
}

MixedClass.prototype = settings.prototypeStrategy === 'copy'
Expand Down
51 changes: 15 additions & 36 deletions test/integration/init-function.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,58 +83,37 @@ describe('Using an init function', () => {
}
});

it('should work with multiple layers of inheritance', () => {
it('should receive proper `this` in the Mixin(A, Mixin(B, C)) scenario', () => {
settings.initFunction = 'init';

abstract class ClassA {
public name;
class ClassA {
public initContextA = null;
protected init() {
this.name = this.constructor.name;
this.initContextA = this;
}
}

class ClassB extends ClassA {
public name1;
class ClassB {
public initContextB = null;
protected init() {
super.init();
this.name1 = this.name + 1;
this.initContextB = this;
}
}

class ClassC extends ClassA {
public name2;
class ClassC {
public initContextC = null;
protected init() {
super.init();
this.name2 = this.name + 2;
this.initContextC = this;
}
}

class ClassD extends ClassA {
public name3;
protected init() {
super.init();
this.name3 = this.name + 3;
}
}

class ClassE extends Mixin(ClassB, ClassC) {}
class ClassF extends Mixin(ClassD, ClassE) {}

const e = new ClassE();
const f = new ClassF();
class ClassD extends Mixin(ClassA, Mixin(ClassB, ClassC)) {}

expect(e).to.deep.equal({
name: 'ClassE',
name1: 'ClassE1',
name2: 'ClassE2',
});
const d = new ClassD();

expect(f).to.deep.equal({
name: 'ClassF',
name1: 'ClassF1',
name2: 'ClassF2',
name3: 'ClassF3',
});
expect(d.initContextA).to.equal(d);
expect(d.initContextB).to.equal(d);
expect(d.initContextC).to.equal(d);
});
});
});

0 comments on commit 0ba1128

Please sign in to comment.