
Fun with Stamps. Episode 16. TypeScript mix-in classes vs Stamps
Hello. I’m developer Vasyl Boroviak and welcome to the sixteenth episode of Vasyl Boroviak presents Fun with Stamps.
TL;DR
In the bottom you will find my fantasy syntax of stamps if it was part of the TypeScript.
What are TypeScript mix-in classes?
Recently, the TypeScript v2.2 released a cool new feature — mix-in classes. Basically, you can “mix classes” now. This is exactly the purpose of stamps — “mix stamps”.
TypeScript is a great language. It gives you tight control over your object types, which is often a beneficial feature.
In this episode the only thing I would like to point out is that the “mix-in classes” feature is hard to read. Of course it depends on your TypeScript skills. But let’s imagine you are a mediocre lazy developer who’s reading the code first time ever.
Here is the official example from TypeScript v2.2 release notes.
How much time does it take you to understand what’s going on here?

Let’s rewrite it using stamps.
How much time you, my imaginary mediocre developer, need to understand the code below?
import stampit from '@stamp/it';const Point = stampit({
init ({x, y}) {
this.x = x;
this.y = y;
}
});const Person = stampit({
init ({name}) {
this.name = name;
}
});const Tagged = stampit({
init ({_tag}) {
this._tag = _tag;
}
});const TaggedPoint = stampit(Point, Tagged);let point = TaggedPoint({x: 10, y: 20, _tag: 'hello'});const Customer = stampit(Person, Tagged, {
init ({accountBalance = 0}) {
this.accountBalance = accountBalance;
}
});let customer = Customer({accountBalance: 0, _tag: 'test'});
In stamps you can provide all the arguments to the initializer (aka constructor). In TypeScript you have to assign the properties after you created an object.
But most important, which is more readable to you?
Fantasy syntax of TypeScript
TypeScript has a whole bunch of other amazing features. But I wish stamps where part of TypeScript rather than a separate module.
Example syntax:
stamp Point {
constructor({public x: number, public y: number}) {}
}stamp Person {
constructor({public name: string}) {}
}stamp Tagged {
constructor({public _tag: string}) {}
}stamp TaggedPoint extends Point, Tagged;let point = TaggedPoint({x: 10, y: 20, _tag: 'hello'});stamp Customer extends Person, Tagged, {
constructor({public accountBalance: number = 0}) {}
};let customer = Customer({accountBalance: 0, _tag: 'test'});
That’s a good start! Let’s fantasise more. Let’s convert the simple stamps from the very first episode of Fun with Stamps. Properties, methods, initializers:
stamp HasFoo {
public foo: number = 'default foo';
}stamp PrintFoo {
public printFoo() {
console.log(this.foo || 'There is no foo');
}
}stamp InitFoo {
constructor({public foo: string}) {}
}stamp Foo extends HasFoo, PrintFoo, InitFoo; // This should work,
const Foo = stamp(HasFoo, PrintFoo, InitFoo); // and this too.const obj = Foo('incoming foo!');
obj.printFoo(); // incoming foo!
How about statics?
stamp TraceStampMetaData {
public static trace() {
console.log(`This stamp consists of ${this.stamp}`);
}
});stamp Foo2 extends Foo, TraceStampMetaData; // These two lines
const Foo3 = stamp(Foo, TraceStampMetaData); // are identical.Foo2.trace(); // prints Foo2.stamp meta-data
Foo3.trace(); // prints Foo3.stamp meta-data
It would be awesome if developers could choose between the stamp() and extends syntax at will. Just like we can choose between the .then() and await syntax in ES6.
The other bits of TypeScript should work with stamps same as they work with classes (generics, interfaces, etc).
That is very appealing. This syntax would convince me to go TypeScript full time.
Differences between TypeScript classes and Stamps
- With stamps the syntax is becoming simpler and shorter.
- With stamps you can have as many constructors as you wish. All will receive exactly the same arguments.
- In stamps there is no inheritance chain, — that’s the main reason why syntax is much simpler. All the methods do go to the
__proto__of your object instances for performance reasons. - You can do multiple inheritance.
- Stamps do not require the
newkeyword. Thus, can be replaced with a simple factory function if needed. - With stamps you have direct access to your stamp’s meta-data. See here. You can build smarter things having that information.
- The
instanceofwon’t work with stamps by design. (If really needed I’d recommend implementing TypeScriptobj is Fookeyword asobj.__proto__ === Foo.stamp.methods. But better not to have it at all.) - … I was trying hard to find downsides of the stamp approach, but failed. Please, help me …
Have fun with stamps!
- Episode 1. Stamp basics
- Episode 2. Dependency injection in FP
- Episode 3. Comparing with the ES2015 classes
- Episode 4. Implementing stamps yourself in 30 LOC
- Episode 5. Composition design pattern
- Episode 6. Statics — properties on stamps
- Episode 7. Early and late dependency injection
- Episode 8. Tracking and overriding composition
- Episode 9. Detaching compose()
- Episode 10. My stamp mental model
- Episode 11. Interfering composition
- Episode 12. New @stamp home
- Episode 13. Method collision control
- Episode 14. New @stamp/it as a replacement of Stampit
- Episode 15. The @stamp/ modules ecosystem
- Episode 16. TypeScript mix-in classes vs Stamps (this article)
- Episode 17. Easy 100% unit test coverage in JS
- Episode 18. Dependency injection paradise
- Episode 19. Java/C# abstract methods in JavaScript
- Episode 20. Stampit v4
- Episode 21. Private data in JavaScript. 4 ways using stamps
- Episode 22. JavaScript instanceof as composable stamp
- Episode 23. New stampit.js.org with all the docs
- Episode 24. New “name” feature
