← Back to TypeScript Course | Chapter 22: Mixins | Lesson 4 of 4

Alternative Mixin Approaches

Mixins can be implemented in several ways depending on the application's needs. Besides class factories, you can use object composition, interfaces, or helper functions to combine behavior.

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

typescript
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

typescript
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

typescript
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

typescript
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

typescript
// 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:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.