TYPESCRIPT
TypeScript Mixin: Syntax, Usage, and Examples
TypeScript mixins allow you to reuse functionality across multiple classes without using traditional inheritance. They provide a way to compose behavior dynamically, making code more modular and maintainable. Mixins in TypeScript are particularly useful when multiple classes need to share logic but do not fit into a strict class hierarchy.
How to Use TypeScript Mixins
A mixin is a function that adds properties or methods to a class. Since TypeScript does not support multiple inheritance, mixins work by copying properties from one class to another.
Basic Mixin Structure
type Constructor<T = {}> = new (...args: any[]) => T;
function Timestamped<T extends Constructor>(Base: T) {
return class extends Base {
timestamp = new Date();
};
}
class Message {
content: string;
constructor(content: string) {
this.content = content;
}
}
const TimestampedMessage = Timestamped(Message);
const message = new TimestampedMessage("Hello");
console.log(message.content); // "Hello"
console.log(message.timestamp); // Current timestamp
When to Use TypeScript Mixins
Mixins are useful when you need to:
- Share methods across unrelated classes without modifying their prototypes.
- Extend class functionality dynamically at runtime.
- Avoid deep inheritance trees by composing behavior flexibly.
Examples of TypeScript Mixins
Using Multiple Mixins
You can combine multiple mixins using function composition.
function Loggable<T extends Constructor>(Base: T) {
return class extends Base {
log() {
console.log(`Logging: ${JSON.stringify(this)}`);
}
};
}
class Task {
description: string;
constructor(description: string) {
this.description = description;
}
}
const LoggableTask = Loggable(Task);
const task = new LoggableTask("Complete project");
task.log();
// Output: Logging: {"description":"Complete project"}
TypeScript Class Mixins
The applyMixins
function allows multiple mixins to be applied to a class.
function applyMixins(targetClass: any, mixinClasses: any[]) {
mixinClasses.forEach(mixin => {
Object.getOwnPropertyNames(mixin.prototype).forEach(name => {
targetClass.prototype[name] = mixin.prototype[name];
});
});
}
class Printable {
print() {
console.log("Printing...");
}
}
class Savable {
save() {
console.log("Saving...");
}
}
class Report {}
applyMixins(Report, [Printable, Savable]);
const report = new Report() as Report & Printable & Savable;
report.print(); // "Printing..."
report.save(); // "Saving..."
TypeScript Mixin Abstract Class
A mixin can extend an abstract class to enforce structure while allowing flexibility.
abstract class DataModel {
abstract save(): void;
}
function TrackChanges<T extends Constructor<DataModel>>(Base: T) {
return class extends Base {
changes: string[] = [];
recordChange(change: string) {
this.changes.push(change);
}
};
}
class UserModel extends DataModel {
save() {
console.log("User saved.");
}
}
const TrackedUserModel = TrackChanges(UserModel);
const user = new TrackedUserModel();
user.recordChange("Updated email");
console.log(user.changes); // ["Updated email"]
user.save(); // "User saved."
TypeScript Mixin Constructor
You can enforce a specific constructor structure using an interface.
interface Named {
name: string;
}
function NamedMixin<T extends Constructor<Named>>(Base: T) {
return class extends Base {
getDisplayName() {
return `User: ${this.name}`;
}
};
}
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
const NamedPerson = NamedMixin(Person);
const person = new NamedPerson("Alice");
console.log(person.getDisplayName()); // "User: Alice"
TypeScript Mixins Example
The following example combines multiple mixins with different functionality.
function CanFly<T extends Constructor>(Base: T) {
return class extends Base {
fly() {
console.log("Flying...");
}
};
}
function CanSwim<T extends Constructor>(Base: T) {
return class extends Base {
swim() {
console.log("Swimming...");
}
};
}
class Superhero {}
applyMixins(Superhero, [CanFly, CanSwim]);
const hero = new Superhero() as Superhero & { fly: () => void; swim: () => void };
hero.fly(); // "Flying..."
hero.swim(); // "Swimming..."
Learn More About TypeScript Mixins
Mixin vs. Inheritance
Unlike traditional class inheritance, where a child class extends a single parent class, mixins allow multiple behaviors to be merged into one class. This prevents deep class hierarchies and allows more modular code reuse.
Using Mixins in TypeScript Libraries
Many libraries use mixins to extend functionality dynamically. For example, UI frameworks like Vue and React use mixins to share component logic.
function Reactive<T extends Constructor>(Base: T) {
return class extends Base {
reactiveData = {};
};
}
class Component {}
const ReactiveComponent = Reactive(Component);
const comp = new ReactiveComponent();
comp.reactiveData = { theme: "dark" };
console.log(comp.reactiveData); // { theme: "dark" }
Limitations of Mixins
- Mixins can make code harder to debug if too many behaviors are added.
- They do not support proper method overriding, so conflicts may arise when two mixins define the same method.
- Type inference might become complex when applying multiple mixins.
TypeScript mixins provide a powerful way to share behavior across multiple classes without deep inheritance trees. You can use them to extend functionality dynamically, create reusable logic, and simplify code structure.
Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.