Alternative Mixin Approaches
In this page:
Object Composition
Object composition combines independent objects directly instead of creating a new subclass — this is often the simplest option when the added behavior doesn't need to participate in class inheritance at all.
Example: Object Composition
function withLogger(obj: object) {
return { ...obj, log: (msg: string) => console.log(msg) };
}
const service = withLogger({ name: "Api" });
service.log("Composed via object spread, no class involved");
Interface Composition
Interfaces can describe a combined capability purely at the type level without changing anything about the actual runtime object — useful when an implementation already exists and only its type shape needs describing.
Example: Interface Composition
interface Named { name: string; }
interface Timestamped { createdAt: Date; }
type AuditedRecord = Named & Timestamped;
const record: AuditedRecord = { name: "Order1", createdAt: new Date() };
console.log(record);
Mixin Class Factories
Class factories (the mixin-function pattern) are the right tool when the mixed-in behavior needs to participate in real inheritance and work naturally with class instances, especially across many different classes.
Example: Mixin Class Factories
type Constructor<T = {}> = new (...args: any[]) => T;
function Loggable<TBase extends Constructor>(Base: TBase) {
return class extends Base { log() { console.log("logging"); } };
}
class Service {}
class LoggableService extends Loggable(Service) {}
new LoggableService().log();
Functional Behavior Composition
Small functions can compose behavior without any classes involved at all — a good alternative whenever the data and behavior are more naturally modeled as plain objects and standalone functions instead.
Example: Functional Behavior Composition
function createCounter() {
let count = 0;
return {
increment: () => ++count,
get: () => count,
};
}
const counter = createCounter();
counter.increment();
console.log(counter.get());
Choosing an Approach
Reach for class mixins when you specifically need reusable class behavior tied into inheritance; prefer object or functional composition when the behavior doesn't depend on inheritance and simpler data-oriented code fits better.
Example: Choosing an Approach
// Need real inheritance + class instances -> mixin class factory
// Otherwise -> plain object/functional composition (simpler)
function withId<T extends object>(obj: T, id: number) {
return { ...obj, id };
}
console.log(withId({ name: "Item" }, 1));
Chapter Quiz — Complete all 4 topics to unlock
0/4 topics done
Complete these topics first: