TypeScript (and JavaScript) classes support strict single inheritance. So you cannot do:
class User extends Tagged, Timestamped { // ERROR : no multiple inheritance}
Another way of building up classes from reusable components is to build them by combining simpler partial classes called mixins.
The idea is simple, instead of a class A extending class B to get its functionality, function B takes class A and returns a new class with this added functionality. Function B
is a mixin.
[A mixin is] a function that
takes a constructor,
creates a class that extends that constructor with new functionality
returns the new class
A complete example
// Needed for all mixinstype Constructor<T = {}> = new (...args: any[]) => T;////////////////////// Example mixins////////////////////// A mixin that adds a propertyfunction Timestamped<TBase extends Constructor>(Base: TBase) {return class extends Base {timestamp = Date.now();};}// a mixin that adds a property and methodsfunction Activatable<TBase extends Constructor>(Base: TBase) {return class extends Base {isActivated = false;activate() {this.isActivated = true;}deactivate() {this.isActivated = false;}};}////////////////////// Usage to compose classes////////////////////// Simple classclass User {name = '';}// User that is Timestampedconst TimestampedUser = Timestamped(User);// User that is Timestamped and Activatableconst TimestampedActivatableUser = Timestamped(Activatable(User));////////////////////// Using the composed classes////////////////////const timestampedUserExample = new TimestampedUser();console.log(timestampedUserExample.timestamp);const timestampedActivatableUserExample = new TimestampedActivatableUser();console.log(timestampedActivatableUserExample.timestamp);console.log(timestampedActivatableUserExample.isActivated);
Let's decompose this example.
Mixins take a class and extend it with new functionality. So we need to define what is a constructor. Easy as:
// Needed for all mixinstype Constructor<T = {}> = new (...args: any[]) => T;
Pretty easy:
// A mixin that adds a propertyfunction Timestamped<TBase extends Constructor>(Base: TBase) {return class extends Base {timestamp = Date.now();};}
And that is it 🌹