TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. In this blog post, we will deep dive into the […]
TypeScript is
a typed superset of JavaScript that compiles to plain JavaScript. In this blog post, we will deep dive into the mixin pattern in TypeScript that we use heavily at Bryntum for development of our products. It allows us to write very clean code and it is very similar to “typeclasses” in Haskell, “traits” in Rust and other similar “high-end” code structuring abstractions.
For the impatient and for future references we’ll provide a summary right away. Scroll below for the gentle introduction.
Cheat sheet
// mixin function
export const MyMixin =
<T extends AnyConstructor<AlreadyImplements & BaseClass>>(base : T) =>
// internal mixin class
class MyMixin extends base {
someProperty : string = 'initialValue'
someMethodFromAlreadyImplementsMixin (arg : number) {
const res = super.someMethodFromAlreadyImplementsMixin(arg)
// ...
return res + 1
someMethodToBeImplementedInTheConsumingClass () : number {
throw new Error('Abstract method called')
someNewMethod () {
if (this.someMethodToBeImplementedInTheConsumingClass() === 42) {
this.methodFromTheBaseClass()
// the "instance type" of this mixin
export type MyMixin = Mixin<typeof MyMixin>
// or, alternatively (see the `Recursive types problem` section below for details)
export interface MyMixin extends Mixin<typeof MyMixin> {}
// "minimal" class builder
export const BuildMinimalMyMixin =
(base : typeof BaseClass = BaseClass) : AnyConstructor<MyMixin> =>
AlreadyImplements(
export class MinimalMyMixin extends BuildMinimalMyMixin() {}
// or, alternatively
export const MinimalMyMixin = BuildMinimalMyMixin()
export type MinimalMyMixin = InstanceType<typeof MinimalMyMixin>
Supporting definitions:
export type AnyFunction<A = any> = (...input: any[]) => A
export type AnyConstructor<A = object> = new (...input: any[]) => A
export type Mixin<T extends AnyFunction> = InstanceType<ReturnType<T>>
So what is wrong with old good “classes”? The main limitation of the classic class pattern is that it normally allows only a single super class. But the real world is not structured like that. In the real world, seemingly unrelated entities can easily share behaviour. For example, the same physic and aerodynamic laws defines the behaviour of some entity with wings. However, with a single super class you will have a hard time trying to isolate such behaviour into a reusable class, and then describing it as an aircraft or a bird.
Mixins solve exactly this problem and the hypothetical mixin “Winged” (or “HasWings”) can be easily applied to (or “mixed in”, or “consumed by”) any class.
From a mathematical point of view, one can say that the classic, single super-class inheritance creates a tree.
And mixin pattern creates a directed acyclic graph.
As you can imagine, arbitrary mixing of different kinds of behaviour can quickly turn into a mess. But the static typing in TypeScript prevents this problem completely, and behaviour mixing becomes very predictable and type-safe. The compiler warns you about any inconsistencies in your mixin code.
Prior art
Surprisingly, the official TypeScript documentation contains a very outdated introduction to mixins. It uses an ad-hoc copying of properties from prototype to prototype, which does not allow proper composition of methods (calling super won’t work). We do not recommend using this pattern.
There is a much better introduction by Marius Schulz, as part of his excellent “TypeScript Evolution” series. It uses the computed base class expression, allows proper method composition and is completely type-safe. Please read this article, as our post is based on the same technique with some improvements for additional type-safety.
Mixin pattern
Now, let’s go through the summary mixin definition from above in details. Let’s write it a bit differently for clarity:
export const MyMixinFunction =
<T extends AnyConstructor<AlreadyImplements>>(base : T) =>
class MyMixinClass extends base {
return MyMixinClass
export type MyMixinType = Mixin<typeof MyMixinFunction>
The Mixin function
The Mixin function (MyMixinFunction) is a function that accepts a constructor of the base class (base argument), creates a subclass of that base class (MyMixinClass) and returns it. Here we did not use the compact arrow function notation and instead went for the full one, using {} brackets and an explicit return statement.
The type of the base argument is T extends AnyConstructor<AlreadyImplements> which should be read as – “any constructor function of a class that already implements (or “has consumed”) the AlreadyImplements mixin. We could specify several such requirements using &: AnyConstructor<AlreadyImplements1 & AlreadyImplements2>. If there are no requirements, we should use the object type, indicating any non-primitive type.
The Mixin class
The name MyMixinClass won’t be available outside of the mixin function – it’s like a local variable inside it. We could omit it completely, but it’s useful to have it for debugging purposes.
All the properties and methods from the AlreadyImplements1 and AlreadyImplements2 mixins will be available in the MyMixinClass class. You can override those and call the super method when / if needed. One can also define new properties and methods or upgrade the type of the inherited properties. You can also define an abstract method – a method which the consuming class must override. Please refer to the summary in the beginning of the post for examples.
The Mixin instance type
The Mixin instance type MyMixinType is an abstract type, indicating an instance of any class that has consumed this mixin. None of the actual values in the code will have this type. But the actual values will have types that extends this type.
MyMixinType is constructed with the Mixin type alias. If you look at its definition in turn, you’ll see that MyMixinType is defined as an instance type of the mixin class returned from the mixin function, which totally makes sense. We get the type of the mixin function with the typeof built-in.
There’s also an alternative notation for the mixin instance type, using interfaces, which solves the problem with recursive type definitions (see below).
Now when having a mixin instance type, we can define a function, that expects an instance of the MyMixinType as an argument:
Here we declare that someFunction, as its first argument, expects any object instance that implements MyMixinType. When using the first argument we restrict ourselves only to properties and methods available in the MyMixinType. The example above will immediately cause a compilation error:
TS2339: Property 'quantity' does not exist on type 'MyMixinFunction<AnyConstructor<AlreadyImplements<AnyConstructor<BaseClass>>.AlreadyImplements & BaseClass>>.MyMixinClass & AlreadyImplements<AnyConstructor<BaseClass>>.AlreadyImplements & BaseClass'
This is because the quantity property is not defined neither in our MyMixinType nor in the AlreadyImplements. This error demonstrates how TypeScript prevents us from using arbitrary properties / methods on a variable with the MyMixinType type.
All together
Considering that TypeScript has different namespaces for values and types, along with the fact that the name of the mixin class (MyMixinClass) is not available outside of the mixin function, we can just use the same name for everything – MyMixin. Such naming creates less cognitive overhead. Most of the time when referencing the name MyMixin we’ll be meaning the mixin instance type (MyMixinType).
If we use the compact notation for the arrow function, we can remove the {} brackets and return statement. Now we can write the compact notation for the mixin pattern as follows:
export const MyMixin = <T extends AnyConstructor<object>>(base : T) =>
class MyMixin extends base {
export type MyMixin = Mixin<typeof MyMixin>
Minimal class builder
We found it very useful to define a “minimal class builder” function for most of our mixins. This is a function that builds the smallest possible class that implements this mixin. Obviously, such a class will only contain the required “super” mixins and our mixin itself. The number of “super” mixins can be quite big, and it is convenient to write all of them on separate line. In such a “builder” function, it is also convenient to specify the default base class as the default argument for the function. If there’s no default base class, you can choose Object:
Now, if we want to apply the mixin to some base class, we just call the builder function.
Important. We found that the minimal class builder function should always have a specified return type. Otherwise TypeScript infers the type automatically, but it seems the inferred type is overly complex – the compilation time increases unacceptably. Specifying the return type manually however fixes this problem.
a Minimal class
We also found it useful to define a “minimal” class for every mixin. This is just a result from the “minimal class builder” in the previous section. It can be declared in two forms – either as a class declaration
export class MinimalMyMixin extends BuildMinimalMyMixin() {}
or as a constant. In the latter case, we also need to create a type for this constant to be able to use it in other places:
The presented mixin pattern is not new and is already used in the JavaScript world. However, because of its fully dynamic nature, JavaScript can not provide the type safety of TypeScript. Because of that, the abstractions can very easily leak from one mixin to another (since the final class is usually built from several mixins). Also, programmers sometimes use the mixin pattern mechanically, just to reduce the file size by moving some of the methods from a big class to an external file containing the mixin.
TypeScript delivers much more, its static typification allows us to define a precisely encapsulated behaviour and compose it in a type safe way.
To illustrate, let’s try to “emulate” some base Haskell typeclasses with mixins. We can start with Eq:
Note, how equal and notEqual method are recursively defined through each other. The consuming side only needs to define one of them and gets another one for free!
Then it goes Ord. It requires the consuming class to already implement Eq and define one of lessOrEqual or compare.
export enum Ordering {
export const Ord = <T extends AnyConstructor<Eq>>(base : T) =>
class Ord extends base {
lessOrEqual (another : this) : boolean {
if (this.equal(another) || this.compare(another) === Ordering.LT) return true
return false
compare (another : this) : Ordering {
if (this.equal(another)) return Ordering.EQ
if (this.lessOrEqual(another))
return Ordering.LT
return Ordering.GT
export type Ord = Mixin<typeof Ord>
Note, how inside the compare method we’ve used the equal method from the base mixin Eq. This typechecks correctly. If however, we would try to use some arbitrary call, it would raise a compilation error.
Now we can define a sort function, which works with any objects having the Ord mixin:
const sort = (array : Ord[]) : Ord[] => {
return array.slice().sort((a, b) => {
a.equal // typechecks
a.zequal // compilation error
switch (a.compare(b)) {
case Ordering.LT: return -1;
case Ordering.EQ: return 0;
case Ordering.GT: return 1;
Again, notice how TypeScript is smart enough to figure out the type of the a and b arguments of the sorter function. This ensures that we can only use methods from Ord and Eq on them.
Now, we can define a Person class, instances of which are ordered by age:
class Person extends Ord(Eq(Object)) {
age : number
constructor (age : number) {
super(...arguments)
this.age = age
equal (another : this) {
return this.age === another.age
lessOrEqual (another : this) : boolean {
return this.age <= another.age
const person1 = new Person(11)
const person2 = new Person(22)
const sorted = sort([ person2, person1 ])
Limitations of the mixin pattern
The mixin pattern is very close to the high-end code structuring abstractions available in other languages like Haskell or Rust. The differences are:
Typeclasses can be implemented for the built-in types. In TypeScript, the best notation for a mixin working with a built-in type, like Array or Map is yet to be determined
In Haskell, the namespaces of the typeclasses are different. This means typeclasses can use the same function name for completely non-related functionality. In TypeScript, you will get name conflicts. This issue happens very rarely in our experience however, and can always be fixed by choosing a different name for some property or method.
Drawbacks
The mixin pattern is currently at the cutting edge of TypeScript type checker possibilities. This unfortunately means that the support for mixins in the TypeScript is not first-class yet. Below you can read about the problems we experienced when using the mixin pattern extensively instead of classic inheritance. They are listed in the order of importance, starting with the most important ones. We encourage you to +1 the issues mentioned below (on GitHub) to help to draw the attention of the TypeScript team to improve the mixin support.
Compilation time
The compilation time for an application built with mixins can increase significantly even in a mid-size project. It feels like some algorithm in the compiler has quadratic behaviour based on the number of the mixins defined. So at some point, every new mixin added to your application slows down the compilation more and more.
The newly appeared “composite projects” feature of the TypeScript could come to the rescue, however, this issue blocks its usage for any non-trivial typed code.
Recursive Types problem
It is perfectly valid to define the recursively typed class definitions in TypeScript:
export class Class1 {
another : Class2
export class Class2 {
another : Class1
However, the same pattern for mixins does not compile:
export const SampleMixin1 = <T extends AnyConstructor<object>>(base : T) =>
class SampleMixin1 extends base {
another : SampleMixin2
export type SampleMixin1 = Mixin<typeof SampleMixin1>
export const SampleMixin2 = <T extends AnyConstructor<object>>(base : T) =>
class SampleMixin2 extends base {
another : SampleMixin1
export type SampleMixin2 = Mixin<typeof SampleMixin2>
This problem has been reported in this issue. The need for such typing appears pretty often. The workaround is to use an alternative notation for the mixin type instance with interfaces:
export const SampleMixin3 = <T extends AnyConstructor<object>>(base : T) =>
class SampleMixin3 extends base {
another : SampleMixin4
export interface SampleMixin3 extends Mixin<typeof SampleMixin3> {}
export const SampleMixin4 = <T extends AnyConstructor<object>>(base : T) =>
class SampleMixin4 extends base {
another : SampleMixin3
export interface SampleMixin4 extends Mixin<typeof SampleMixin4> {}
It is not clear however, if this notation affects the compilation time. Also this notation does not work cross-project. If you use it, you need to include all source files (even from another packages) into the include config in your tsconfig.json.
Generic argument problem
Currently it is not trivial to define a mixin with a generic argument. Let’s say we want to define a generic mixin wrapper for a value of arbitrary type V. The most natural definition would be:
export const Atom = <V, T extends AnyConstructor<object>>(base : T) =>
class Atom extends base {
value : V
export type Atom<V> = Mixin<typeof Atom<V>>
However, because of this issue, it does not compile. Basically the type application Atom<V> is only possible during a function call: Atom<V>() and can not be used in the context of value const AtomDate = Atom<Date>.
The workaround, (which works only on the class-level) is to use the extra dummy property VALUE_TYPE, typed as any in the base mixin. Everywhere where we need to reference the type of the value property we use this[ 'VALUE_TYPE' ] instead (this[ 'value' ] will work too actually).
export const Atom = <T extends AnyConstructor<object>>(base : T) =>
class Atom extends base {
VALUE_TYPE : any
value : this[ 'VALUE_TYPE' ]
// the "instance type" of this mixin
export type Atom = Mixin<typeof Atom>
Then, on the consuming side (a final class, or another mixin), we can upgrade the type of the VALUE_TYPE property:
class AtomDate extends Atom(Object) {
VALUE_TYPE : Date
Here, the VALUE_TYPE property plays the role of a generic type argument.
Decorators
When using decorators in a mixin, one can not use the compact arrow function notation, and is forced to use the full one.
export const SampleMixin1 = <T extends AnyConstructor<object>>(base : T) => {
class SampleMixin1 extends base {
@decorator
property : number
return SampleMixin1
export type SampleMixin1 = Mixin<typeof SampleMixin1>
This is a minor issue compared to the others, it simply causes one extra indentation level in the code. However, it would be great to see it solved.
Advanced usage
There’s nothing preventing us from supplying additional arguments for a mixin function, or for example, applying the same mixin function more than once. If we combine this with computed properties, we can implement various quite advanced scenarios. Of course, this should be used with care to not pollute your codebase with extra cognitive overhead, just because “it’s cool”. But a valid use case can be:
export const HasUniqueId =
<T extends AnyConstructor<object>>(base : T, idProperty: string | symbol = 'id') =>
let ids : number = 1
class HasUniqueId extends base {
// @ts-ignore
[ idProperty ] : number = ids++
return HasUniqueId
export type HasUniqueId = Mixin<typeof HasUniqueId>
Unfortunately in the example above, TypeScript insists that TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol', so we have to convince it, that we know what we are doing with // @ts-ignore
To create a class implementing the Atom mixin from the previous example with a unique id in the non-standard ID property, we can do:
class AtomWithId extends HasUniqueId(Atom(Object), 'ID') {
Conclusion
In this post we demonstrated that the mixin pattern in TypeScript is comparable to more advanced code composition abstractions, found in Haskell (typeclasses), Rust (traits) and other languages. We provided a complete notation that scales well (type-safety wise) by the number of mixins, along with examples of its usage. The presented notation has been successfully used in our Bryntum products.
We also demonstrated the advantages of the mixin pattern over the classical single super class inheritance.
Lastly, we also highlighted some current drawbacks of using this approach, notably the increased compilation time and problems with recursive types and generic type arguments. We hope that the mixin pattern will become more widespread and that the support for it in TypeScript will become first-class. We encourage you to +1 the Github issues mentioned in this post to get attention from the TypeScript team.
In the meantime, if you find that some reusable logic in your application is tied too strictly to a certain super-class, you should definitely consider rewriting it as a mixin. And TypeScript will then ensure it is correctly used in every consuming class.
Happy mixing!
Great article but I’m having some problems applying the pattern with Typescript 3.4.3. Am I ahead or behind the version you are currently using with this pattern?
Specifically:
The two alternative ways of defining the mixin’s ‘instance type’ both give the following error:
‘Type instantiation is excessively deep and possibly infinite.ts(2589)’
The compact arrow notation version works but the full version (with the braces and return) give the error:
‘Exported variable ‘SampleMixin1’ has or is using private name ‘SampleMixin1′.ts(4025)’
Consequently, it is not possible to use decorators.
This is such an awesome detailed work, thanks . About “, how equal and notEqual method are recursively defined through each other. The consuming side only needs to define one of them and gets another one for free!” … mmm .. Is it not supposed that type-declarations cannot be recursive – I mean a definition should be independent of itself ? I’ve seen many people which their amazint typings helper (that performed the job but increased considerably compilation time), sudenttly stop working because of a TypeScript compiler issue being fixed. .. I don’t have other option than use mixins but I probably will take the simpler alternative, although with that one is complicated / imposible to declare a properly typed constructor (which is usually important “A mixin class must have a constructor with a single rest parameter of type ‘any[]'”) – do you have any tips for constructors ?
Glad to hear, thank you!
`equal` and `notEqual` are fine – the consuming side is always expected to override one of them, so there will be no endless recursion. Type-wise that is also safe, because method don’t reference other mixins. There is a problem with recursive references to other mixins, mentioned in the post, thankfully, the workaround with interfaces seems to work well, with new TypeScript versions too.
As about constructors, I use the following approach – mixins do not have constructors. Instead, the concrete classes, created from mixins, inherit from the Base class, which has a special static method “new”, which which accepts an object with keys corresponding to properties.
Hi, great article! Unfortunately, it seems that the this[‘VALUE_TYPE’] trick won’t work for production when declaration files are generated. For example, your code gets compiled to
export declare const Atom: (base: T) => {
new (…input: any[]): {
VALUE_TYPE?: any;
value?: any;
export declare type Atom = Mixin;
declare const AtomDate_base: {
new (…input: any[]): {
VALUE_TYPE?: any;
value?: any; // Note the changed type
} & ObjectConstructor;
export declare class AtomDate extends AtomDate_base {
VALUE_TYPE?: Date;
export {};
So, the this[‘VALUE_TYPE’] type is lost during compilation (which is quite logical because the this-type cannot be used in const expressions such as the one generated).
Hi, thanks!
Yes, generics in mixins are not trivial. Declaration files are another issue – public/private mixin methods won’t even compile when those are enabled for example. These are well known problems that, however, aren’t considered important in official TS world.
So combining generic + declaration files moves you to the edge of what is possible with the mixins.