2 min read

TypeScript Mixins

Mixins are a way to build up classes from reusable components. Unlike traditional inheritance where a class extends a single base class, mixins allow you to combine multiple behaviors into a single class.

In TypeScript, mixins are typically implemented using the class factory pattern.

1. The Mixin Pattern

A mixin is essentially a function that:

  1. Takes a constructor (a class) as an input.
  2. Returns a new class that extends the input class with new properties or methods.

Defining a Constructor Type

First, we need a type that represents "any class constructor".

type Constructor<T = {}> = new (...args: any[]) => T;

2. Creating Mixins

Let's create a mixin that adds a timestamp property to any class.

// The Mixin Function
function Timestamped<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    timestamp = Date.now();
  };
}

3. Usage Example

Let's apply this to a basic class.

class User {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

// Create a new class by applying the Mixin
const TimestampedUser = Timestamped(User);

// Instantiate
const user = new TimestampedUser("Alice");
console.log(user.name);      // "Alice"
console.log(user.timestamp); // 169... (Current timestamp)

4. Multiple Mixins (Composition)

You can chain mixins to add multiple capabilities.

// Mixin 1: Adds activation capability
function Activatable<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    isActive = false;
    activate() {
      this.isActive = true;
    }
    deactivate() {
      this.isActive = false;
    }
  };
}

// Base Class
class Sprite {
  name = "";
}

// Compose them
const SuperSprite = Activatable(Timestamped(Sprite));

const player = new SuperSprite();
player.activate();        // From Activatable
console.log(player.timestamp); // From Timestamped

5. Constraints

Mixins in TypeScript have some limitations:

  1. Constructors: Mixins cannot easily declare new constructor parameters because the mixin function doesn't know what the base class constructor looks like. It's best to keep mixin constructors simple or avoid them.
  2. super calls: You can call super methods, but you have to be careful about the method signatures matching.

programming/javascript/typescript/typescript