Applying Mixins
In this page:
Applying One Mixin
Start with a plain base class and pass its constructor into a mixin function — the constructor that comes back out represents the enhanced class with the mixin's behavior added on top.
Example: Applying One Mixin
type Constructor<T = {}> = new (...args: any[]) => T;
function Flyable<TBase extends Constructor>(Base: TBase) {
return class extends Base { fly() { return "Flying"; } };
}
class Bird {}
class FlyingBird extends Flyable(Bird) {}
console.log(new FlyingBird().fly());
Applying Multiple Mixins
Nesting mixin calls creates a class carrying every capability supplied by each mixin in the chain; the order can matter whenever a later mixin depends on members a mixin earlier in the chain already added.
Example: Applying Multiple Mixins
type Constructor<T = {}> = new (...args: any[]) => T;
function Swimmable<TBase extends Constructor>(Base: TBase) {
return class extends Base { swim() { return "Swimming"; } };
}
function Flyable<TBase extends Constructor>(Base: TBase) {
return class extends Base { fly() { return "Flying"; } };
}
class Duck {}
class SuperDuck extends Swimmable(Flyable(Duck)) {}
const duck = new SuperDuck();
console.log(duck.fly(), duck.swim());
Mixin Composition Function
A small helper function can centralize the application of several mixins at once, turning a chain of nested function calls into one readable, reusable composition step.
Example: Mixin Composition Function
type Constructor<T = {}> = new (...args: any[]) => T;
function Flyable<TBase extends Constructor>(Base: TBase) {
return class extends Base { fly() { return "flying"; } };
}
function Swimmable<TBase extends Constructor>(Base: TBase) {
return class extends Base { swim() { return "swimming"; } };
}
function compose<TBase extends Constructor>(Base: TBase) {
return Swimmable(Flyable(Base));
}
class Animal {}
const Duck = compose(Animal);
console.log(new Duck().fly(), new Duck().swim());
Mixins with Constructors
A mixin can preserve whatever constructor arguments the base class expects by extending that base class directly — the mixin itself should avoid assuming a fixed constructor signature unless it explicitly constrains one.
Example: Mixins with Constructors
type Constructor<T = {}> = new (...args: any[]) => T;
function Named<TBase extends Constructor>(Base: TBase) {
return class extends Base {
constructor(...args: any[]) {
super(...args);
}
};
}
class Person {
constructor(public name: string) {}
}
class NamedPerson extends Named(Person) {}
console.log(new NamedPerson("Ravi").name);
Mixin Usage Patterns
Keep individual mixin functions small and focused on one capability. Create a named, reusable enhanced constructor when a particular combination gets applied repeatedly, or just apply mixins inline for a one-off class.
Example: Mixin Usage Patterns
type Constructor<T = {}> = new (...args: any[]) => T;
function Loggable<TBase extends Constructor>(Base: TBase) {
return class extends Base { log() { console.log("logging"); } };
}
class Service {}
// Applied inline for a one-off class:
class OneOffService extends Loggable(Service) {}
new OneOffService().log();
Chapter Quiz — Complete all 4 topics to unlock
0/4 topics done
Complete these topics first: