TypeScript Mixins: Building Classes from Reusable Parts
A mixin is a function that takes a class and returns a new one with added features, like bolting on a turbocharger. Use it to share behavior (e.g., logging) across unrelated classes.
WHY IT EXISTS Traditional single inheritance can be too restrictive. You might want to add logging capabilities to both a User class and a DatabaseRequest class, but they don't share a common ancestor. Mixins provide a way to share behavior between classes without forcing them into the same inheritance chain.
THE MENTAL MODEL A mixin is a function that takes a class as input and returns a new, enhanced class as output. It's not inheritance; it's composition at the class level. Instead of a class being a certain type, you apply the behavior of that type to the class dynamically.
HOW IT WORKS The pattern uses a function that accepts a generic constructor type. Inside this function, you define a class expression that extends the base class passed in. This new inner class contains the mixin's properties and methods. The function then returns this newly composed class. To apply the mixin, you simply call the function with your base class as the argument, which yields a new class definition that combines both.
WHEN TO USE IT Use mixins to share functionality across different class hierarchies. This is perfect for cross-cutting concerns like serialization, event handling, or timestamping. For example, you can create a Timestampable mixin and apply it to Post, Comment, and User classes to add createdAt and updatedAt fields to all of them without a shared base class.
WHEN NOT TO USE IT Avoid mixins for defining the core identity of a class. If a Dog's essential nature is that it's an Animal, use inheritance. Mixins are for supplemental, non-essential behaviors. Overusing them can create complex, hard-to-debug classes where it's unclear where any given method or property originates.
ONE CANONICAL EXAMPLE Imagine a Sprite class for a game object. To make it scalable, we create a Scale mixin. This is a function, Scale(Base), that returns a new class: class Scaling extends Base { ... }. This Scaling class adds a #scale private field and setScale()/get scale() methods. We then create our final class: const ScalableSprite = Scale(Sprite);. An instance of ScalableSprite now has all the original Sprite features plus the new scaling capabilities. The key footgun is that mixins cannot declare private or protected properties; you must use ES2020 private fields (like #scale) for proper encapsulation.
Read the original → typescriptlang.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.