Simulate inheritance by using type composition
There is no notion of inheritance in MST. The recommended approach is to keep references to the original configuration of a model in order to compose it into a new one, for example by using types.compose
(which combines two types) or producing fresh types using .props|.views|.actions
. An example of classical inheritance could be expressed using composition as follows:
const Square = types
.model(
"Square",
{
width: types.number
}
)
.views(self => ({
// note: this is not a getter! this is just a function that is evaluated
surface() {
return self.width * self.width
}
}))
// create a new type, based on Square
const Box = Square
.named("Box")
.views(self => {
// save the base implementation of surface, again, this is a function.
// if it was a getter, the getter would be evaluated only once here
// instead of being able to evaluate dynamically at time-of-use
const superSurface = self.surface
return {
// super contrived override example!
surface() {
return superSurface() * 1
},
volume() {
return self.surface() * self.width
}
}
}))
// no inheritance, but, union types and code reuse
const Shape = types.union(Box, Square)
const instance = Shape.create({type:"Box",width:4})
console.log(instance.width)
console.log(instance.surface()) // calls Box.surface()
console.log(instance.volume()) // calls Box.volume()
Similarly, compose can be used to simply mix in types:
const CreationLogger = types.model().actions((self) => ({
afterCreate() {
console.log("Instantiated " + getType(self).name)
}
}))
const BaseSquare = types
.model({
width: types.number
})
.views((self) => ({
surface() {
return self.width * self.width
}
}))
export const LoggingSquare = types
.compose(
// combine a simple square model...
BaseSquare,
// ... with the logger type
CreationLogger
)
// ..and give it a nice name
.named("LoggingSquare")