Ts Mixin
Mixin is a code reuse pattern used to mix multiple independent functional modules into a single class.
TypeScript implements Mixin through generic functions that return extended classes, compensating for the inability of single inheritance to reuse behaviors from multiple sources.
* * *
## Basic Concepts
The core of Mixin is a generic function that receives a base class and returns an extended class. The `Constructor` type constraint is used to describe "any class that can be inherited".
## Example
// Constructor type: describes any instantiable class
type Constructor=new(...args: any[])=> T;
// Mixin function: receives a base class, returns an extended new class
function Timestamped(Base: TBase){
return class extends Base {
createdAt =new Date();
};
}
// Base class
class User {
constructor(public name: string){}
}
// New class after mixing in
const TimestampedUser = Timestamped(User);
const user =new TimestampedUser("Alice");
console.log(user.name);// Alice
console.log(user.createdAt instanceof Date);// true
**Output:**
Alicetrue
> **Note:** Mixin does not modify the original class, but returns a completely new extended class. The original `User` class is not affected.
* * *
## Combining Multiple Mixins
By nesting multiple Mixin function calls, multiple capabilities can be stacked onto the same class in sequence.
## Example
type Constructor=new(...args: any[])=> T;
// Mixin 1: adds timestamp
function Timestamped(Base: TBase){
return class extends Base {
createdAt =new Date();
};
}
// Mixin 2: adds serialization
function Serializable(Base: TBase){
return class extends Base {
serialize(): string {
return JSON.stringify(this);
}
};
}
// Mixin 3: adds logging (depends on serialize method)
function Loggable<TBase extends Constructor>(Base: TBase){
return class extends Base {
log():void{
console.log("",this.serialize());
}
};
}
class Product {
constructor(public name: string, public price: number){}
}
// Stack three Mixins in sequence
const AdvancedProduct = Loggable(Serializable(Timestamped(Product)));
const p =new AdvancedProduct("Phone",999);
p.log();
console.log(p.createdAt instanceof Date);// true
**Output:**
{"name":"Phone","price":999,"createdAt":"2026-..."}true
* * *
## Combining Mixin with Interface
The class returned by Mixin can declare that it implements an interface, allowing consumers to operate on the mixed object through the interface type without depending on the concrete implementation.
## Example
type Constructor=new(...args: any[])=> T;
// Define capability interfaces
interface ISerializable {
serialize(): string;
}
interface ICloneable{
clone(): T;
}
// Mixin implements interface
function Serializable(Base: TBase){
return class extends Base implements ISerializable {
serialize(): string {
return JSON.stringify(this);
}
};
}
function Cloneable(Base: TBase){
return class extends Base {
clone(){
return Object.assign(
Object.create(Object.getPrototypeOf(this)),
this
);
}
};
}
class Article {
constructor(public title: string, public content: string){}
}
const RichArticle = Cloneable(Serializable(Article));
const a1 =new RichArticle("TypeScript Getting Started","Main Content...");
const a2 = a1.clone();
a2.title="TypeScript Advanced";
console.log(a1.serialize());
// {"title":"TypeScript Getting Started","content":"Main Content..."}
console.log(a2.serialize());
// {"title":"TypeScript Advanced","content":"Main Content..."}
console.log(a1.title=== a2.title);// falseοΌModify independently after cloning)
**Output:**
{"title":"TypeScript Getting Started","content":"Main Content..."}{"title":"TypeScript Advanced","content":"Main Content..."}false
* * *
## Constrained Mixin
Through generic constraints, Mixin can be limited to only mix into classes that meet specific conditions, avoiding missing necessary properties at runtime.
## Example
type Constructor=new(...args: any[])=> T;
// Constraint: base class must have id and name properties
type WithIdAndName = Constructor;
function Printable(Base: TBase){
return class extends Base {
print():void{
console.log(`[${this.id}] ${this.name}`);
}
};
}
class Item {
constructor(public id: number, public name: string){}
}
// Correct: Item satisfies the constraint
const PrintableItem = Printable(Item);
const item =new PrintableItem(42,"Keyboard");
item.print();// Keyboard
// Error example (compiler will block):
// class NoId { constructor(public name: string) {} }
// const Bad = Printable(NoId); // Error: NoId is missing id property
**Output:**
Keyboard
* * *
## Mixin vs Inheritance Comparison
| Dimension | Inheritance (extends) | Mixin |
| --- | --- | --- |
| Number of Sources | Can only inherit one parent class | Can stack any number |
| Coupling Degree | Strong coupling between subclass and parent class | Each Mixin is independent, low coupling |
| Reuse Granularity | Reuses entire class capabilities | Reuses single functionality on demand |
| Type Safety | Natively supported | Requires generic constraints to ensure |
| Applicable Scenarios | Strong "is-a" relationship | Cross-cutting concerns (logging, serialization, caching, etc.) |
* * *
## Summary
* **Core Pattern:** Mixin is a generic function that receives a base class and returns an extended class. `Constructor` is the standard constraint type
* **Capability Stacking:** Nesting multiple Mixin function calls allows combining multiple capabilities into the same class
* **Interface Combination:** Classes returned by Mixin can implement interfaces. Consumers only depend on interfaces, not concrete classes
* **Generic Constraints:** By constraining `TBase`, the applicable scope of Mixin can be limited, preventing incorrect usage at compile time
* **Applicable Scenarios:** Cross-cutting concerns such as logging, serialization, cloning, timestamps, etc., superior to multi-level inheritance
YouTip