Dyna Dyna - 2 months ago 8
Javascript Question

Mixin in TypeScript to already instantiated Object

I'm using an external library, that returns an instantiated Object

a
of Type
A
in a callback, where
A
is defined as an Interface (the class imlementation of
A
is not exported by the external module):

extLib.on("someEvent", ((a: A) => { /*...*/ });


Now I'd like to add a mixin Object of Type
B
to the already existing instance of
A
:

class B {
someExtension() { /* ... */ }
}


My current approach is somewhat lousy:

function Add_B(a: A): (A & B) {
// cast to intersection type
let _a = a as (A & B);
_a.someExtension = () => { /* ... */ }
return _a;
}

extLib.on("someEvent", ((a: A) => {
let _a = Add_B(a);
// mixin achieved, _a is of type (A & B)
});


Now does someone know of a better approach that would:


  • Allow
    B
    to have callable constructor and have
    B
    be newable

  • Result in less/cleaner code

  • Has more expressive typings than the
    A & B
    intersection type

  • Would allow for
    readonly
    properties on
    B
    ?

  • Doesn't mess with the prototype chain too heavily (think
    static
    members)
    ?


Answer

How about this:

interface A {
    prop: string;
}

class B {
    // so the same function can be shared between instance of B and instances of A & B
    static someExtensionStatic = () => { /* ... */ }

    someExtension = B.someExtensionStatic;
    readonly prop2: number;
    constructor(a?:A) {
        if (a) {
            let a1 = a as AandB;
            a1.someExtension = B.someExtensionStatic;
            return a1;
        }
    }
}

type AandB = A & B;

and in your calling code:

extLib.on("someEvent", ((a: A) => {
    let _a = new B(a) as AandB;
    // mixin achieved, _a is of type (A & B)
}));

Typescript Playground

One issue: the prototype of new instances of B will be at B.prototype, but instances of A will not have the prototype of B.prototype.

Update

A consequence of the difference in prototype will result in the following:

var b1 = new B();
var isInstanceOf = b1 instanceof B; // true

var b2 = new B(a);
isInstanceOf = b2 instanceof B;     // false

Therefore you should decide the type of the resulting object:

  • an instance of A, augmented with the members of B

    In that case new B(a) doesn't make sense; a static B.augment<T>: T & B method would be more fitting

  • an instance of B, augmented with members of A; and instanceof will work sensibly
Comments