Skip to content
DocsTypeScript ExpertexpertMixins
Chapter 1 of 10·expert·10 min read

Mixins

Mixin

Compose reusable behaviours across class hierarchies without multiple inheritance

Hover or tap any paragraph to see Vietnamese translation

What Are Mixins?

A mixin is a pattern that lets you combine behaviour from multiple sources into a single class. TypeScript does not support multiple inheritance — a class cannot extend more than one base class — but mixins solve this elegantly.

Instead of direct inheritance, a mixin is a function that takes a class as input and returns a new, extended class. You can stack multiple mixins to compose a feature-rich class from small, focused pieces.

The Constructor Type

To write type-safe mixins in TypeScript you need a type that represents "any class that can be instantiated". This is the foundation every mixin is built on.

constructor-type.ts
// A generic constructor type
// GInstance defaults to {} so any instance shape is accepted
type GConstructor<GInstance = {}> = new (...args: unknown[]) => GInstance;

GConstructor<T> is a constructor type that accepts any number of arguments and produces an instance of type T. The GInstance type parameter lets you constrain the input class to have a particular shape.

A Basic Mixin

Here is a first mixin: Timestamped. It accepts any class and adds a createdAt property to it.

timestamped-mixin.ts
1type GConstructor<GInstance = {}> = new (...args: unknown[]) => GInstance;23function Timestamped<TBase extends GConstructor>(Base: TBase) {4  return class extends Base {5    createdAt = new Date();6  };7}89// Apply the mixin10class User {11  constructor(public name: string) {}12}1314const TimestampedUser = Timestamped(User);15const user = new TimestampedUser("Alice");1617console.log(user.name);       // "Alice"18console.log(user.createdAt);  // current Date

The Timestamped function takes a class (Base) and returns an anonymous class that extends it with the new property. The return type is inferred by TypeScript, so user has full type information for both name and createdAt.

Composing Multiple Mixins

The real power of mixins appears when you compose them. Each mixin only knows about its own base class and adds one focused capability.

composed-mixins.ts
1type GConstructor<GInstance = {}> = new (...args: unknown[]) => GInstance;23// Mixin 1: adds createdAt4function Timestamped<TBase extends GConstructor>(Base: TBase) {5  return class extends Base {6    createdAt = new Date();7  };8}910// Mixin 2: adds activate / deactivate11function Activatable<TBase extends GConstructor>(Base: TBase) {12  return class extends Base {
Info
The order in which you apply mixins can affect behaviour if multiple mixins define the same property or method. The outermost mixin wins.

Constraining the Input Class

Sometimes a mixin only makes sense applied to classes with a particular shape. You can use a generic constraint to enforce this at compile time.

constrained-mixin.ts
1type GConstructor<GInstance = {}> = new (...args: unknown[]) => GInstance;23// This mixin requires the base class to have a name property4type Named = GConstructor<{ name: string }>;56function Greeter<TBase extends Named>(Base: TBase) {7  return class extends Base {8    greet() {9      return `Hello, I'm ${this.name}`;10    }11  };12}

Mixins and Interfaces

An idiomatic way to declare the type of a composed class is to merge an interface with the class. This lets you describe the resulting shape without relying solely on the inferred type.

mixin-interface.ts
1type GConstructor<GInstance = {}> = new (...args: unknown[]) => GInstance;23function Timestamped<TBase extends GConstructor>(Base: TBase) {4  return class extends Base {5    createdAt = new Date();6  };7}89function Activatable<TBase extends GConstructor>(Base: TBase) {10  return class extends Base {11    isActive = false;12    activate() { this.isActive = true; }

Practical Example: Repository Mixin

Here is a more realistic example: a mixin that adds basic CRUD tracking to any entity class.

auditable-mixin.ts
1type GConstructor<GInstance = {}> = new (...args: unknown[]) => GInstance;23function Auditable<TBase extends GConstructor>(Base: TBase) {4  return class extends Base {5    createdAt: Date = new Date();6    updatedAt: Date = new Date();7    createdBy = "system";89    touch(by: string) {10      this.updatedAt = new Date();11      this.createdBy = by;12    }

Limitations and Pitfalls

No Private Members from the Mixin

Mixins cannot access private members of the base class because they are declared outside it. Use protected if you need access from a mixin.

Reflection and instanceof

Because mixins produce anonymous classes, instanceof may not work as expected against intermediate classes. Check against the final composed class, not individual mixins.

No Automatic Constructor Forwarding

If a mixin needs its own constructor parameters you must handle argument forwarding manually. This is why the ...args: unknown[] signature is used — it is compatible with any base constructor.

mixin-constructor.ts
1type GConstructor<GInstance = {}> = new (...args: unknown[]) => GInstance;23function Tagged<TBase extends GConstructor>(Base: TBase) {4  return class extends Base {5    tag: string;67    constructor(...args: unknown[]) {8      super(...args); // forward all args to the base class9      this.tag = "default";10    }1112    withTag(tag: string): this {

Summary

  • A mixin is a function that takes a class and returns an extended class
  • GConstructor<T> is the foundation type that makes mixins type-safe
  • You can stack multiple mixins: C(B(A(Base)))
  • Use generic constraints to require the base class to have a specific shape
  • Mixins cannot access private members — use protected instead
  • instanceof works against the final composed class, not intermediate mixins
  • Built: 4/8/2026, 12:01:11 PM