type Constructor<T = {}> = new (...args: any[]) => T;
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
createdAt = new Date();
touch(): void { this.createdAt = new Date(); }
};
}
class User { constructor(public name: string) {} }
const TimestampedUser = Timestamped(User);
A mixin is a function that takes a class and returns a new class that extends it with extra members. It lets you add the same feature to unrelated classes, even though JavaScript allows only one parent class.
Timestamped(User) creates a subclass of User that also has createdAt and touch(). You can stack them: Timestamped(SoftDeletable(User)).
When it fits:
- A small feature that really is part of the object's own state, such as timestamps or soft-delete flags on entities.
- Libraries that need it, for example some ORMs and NestJS's
PartialType()/PickType()helpers for DTO classes.
Downsides:
- It is still inheritance. Each layer can clash on names, and
supercalls chain through every layer. - Types and error messages get hard to read.
- Hard to inject or replace in tests.
For behaviour that talks to the outside world (logging, caching, sending email), prefer composition: inject a collaborator.
- A mixin is a function that takes a class and returns a subclass with extra members.
- Mixins let unrelated classes share a feature despite single inheritance.
- They fit small state-like features, such as timestamps on entities.
- They are still inheritance, so name clashes and long
superchains can happen. - Prefer composition for behaviour that uses outside services.
Read more