Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

TypeScript Mixins


May 07, 2021 TypeScript



Introduction to TypeScript Mixins

In addition to the traditional object-oriented inheritance approach, one popular way to create a class through re-usable components is to union the code of another simple class. You may already be familiar with mixins and their features in languages such as Scala, but it's also popular in JavaScript.

Mix in the example

The following code shows how to use blending in TypeScript. We'll explain how this code works later.

// Disposable Mixin
class Disposable {
    isDisposed: boolean;
    dispose() {
        this.isDisposed = true;
    }

}

// Activatable Mixin
class Activatable {
    isActive: boolean;
    activate() {
        this.isActive = true;
    }
    deactivate() {
        this.isActive = false;
    }
}

class SmartObject implements Disposable, Activatable {
    constructor() {
        setInterval(() => console.log(this.isActive + " : " + this.isDisposed), 500);
    }

    interact() {
        this.activate();
    }

    // Disposable
    isDisposed: boolean = false;
    dispose: () => void;
    // Activatable
    isActive: boolean = false;
    activate: () => void;
    deactivate: () => void;
}
applyMixins(SmartObject, [Disposable, Activatable]);

let smartObj = new SmartObject();
setTimeout(() => smartObj.interact(), 1000);

////////////////////////////////////////
// In your runtime library somewhere
////////////////////////////////////////

function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
            derivedCtor.prototype[name] = baseCtor.prototype[name];
        });
    });
}

Understand this example

Two classes are defined in the code first, and they will be used as mixins. Y ou can see that each class defines only one specific behavior or function. We'll use them later to create a new class with both.

// Disposable Mixin
class Disposable {
    isDisposed: boolean;
    dispose() {
        this.isDisposed = true;
    }

}

// Activatable Mixin
class Activatable {
    isActive: boolean;
    activate() {
        this.isActive = true;
    }
    deactivate() {
        this.isActive = false;
    }
}

Let's create a class that combines these two mixins. Here's a look at how:

class SmartObject implements Disposable, Activatable {

The first thing to note is that instead extends you are implements T hink of classes as interfaces, using only the types of Disposable and Activatable, not their implementations. T his means that we need to implement the interface in the class. But that's what we want to avoid when we use mixin.

We can do this to do this by creating a placeholder property for the property method that will come in. T his tells the compiler that these members are available at runtime. This makes it easy to use mixin, although some placeholder properties need to be defined in advance.

// Disposable
isDisposed: boolean = false;
dispose: () => void;
// Activatable
isActive: boolean = false;
activate: () => void;
deactivate: () => void;

Finally, mix mixing the mix into the defined classes completes the implementation.

applyMixins(SmartObject, [Disposable, Activatable]);

Finally, create this help function to help us mix in. It traverses all the properties on mixins and copies them to the target, replacing the previous placeholder properties with real implementation code.

function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
            derivedCtor.prototype[name] = baseCtor.prototype[name];
        })
    });
}