Mixin Pattern
What Is a Mixin?
A mixin adds reusable behavior to a class without requiring multiple inheritance — a mixin function takes a constructor as input and returns a new constructor that has all the original members plus the added ones.
Example: What Is a Mixin?
type Constructor<T = {}> = new (...args: any[]) => T;
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
timestamp = Date.now();
};
}
class Point {}
class TimestampedPoint extends Timestamped(Point) {}
console.log(typeof new TimestampedPoint().timestamp);
Mixin with Methods
A mixin can add one or more methods to whatever class it's applied to; the class returned from the mixin function carries both the original class's members and the new behavior the mixin contributed.
Example: Mixin with Methods
type Constructor<T = {}> = new (...args: any[]) => T;
function Serializable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
serialize() { return JSON.stringify(this); }
};
}
class Point { x = 1; y = 2; }
class SerializablePoint extends Serializable(Point) {}
console.log(new SerializablePoint().serialize());
Combining Mixins
Multiple mixins can be chained one after another, with each mixin function receiving the constructor that the previous mixin in the chain already returned, layering capabilities on top of each other.
Example: Combining Mixins
type Constructor<T = {}> = new (...args: any[]) => T;
function Named<TBase extends Constructor>(Base: TBase) {
return class extends Base { name = "unnamed"; };
}
function Aged<TBase extends Constructor>(Base: TBase) {
return class extends Base { age = 0; };
}
class Base {}
class Person extends Aged(Named(Base)) {}
const p = new Person();
console.log(p.name, p.age);
Mixin State
Mixins can add instance fields as well as methods — those fields belong to each individual instance created from the returned class, so every instance gets its own independent copy of that state.
Example: Mixin State
type Constructor<T = {}> = new (...args: any[]) => T;
function Counter<TBase extends Constructor>(Base: TBase) {
return class extends Base {
count = 0;
increment() { this.count++; }
};
}
class Base {}
class Widget extends Counter(Base) {}
const w1 = new Widget();
const w2 = new Widget();
w1.increment();
console.log(w1.count, w2.count);
Mixin Benefits
Mixins pay off when several otherwise-unrelated classes need the exact same behavior, encouraging small, composable capabilities instead of forcing everything into one deep, rigid inheritance hierarchy.
Example: Mixin Benefits
type Constructor<T = {}> = new (...args: any[]) => T;
function Loggable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
log(msg: string) { console.log(`[LOG] ${msg}`); }
};
}
class Service {}
class ApiService extends Loggable(Service) {}
new ApiService().log("Request received");
Chapter Quiz — Complete all 4 topics to unlock
0/4 topics done
Complete these topics first: