Runtime type for subclasses
class Geometer extends M.Base {
static innerTypes () {
return Object.freeze({
name: string({minLength: 1}),
// Shape has multiple subclasses
favouriteShape: _(Shape)
})
}
}const ShapeType = M.Enum.fromArray(['CIRCLE', 'DIAMOND'])
// We are going to use this metadata in several places, so by reusing it, we
// not only save unnecessary processing, but our generated JSON schema will
// also be more compact.
const greaterThanZero = number({
exclusiveMinimum: 0
})
const reviver = (k, v) => {
if (k !== '') {
return v
}
switch (v.type) {
case ShapeType.CIRCLE().toJSON():
return new Circle(v)
case ShapeType.DIAMOND().toJSON():
return new Diamond(v)
default:
throw TypeError('Unsupported or missing shape type in the Shape reviver.')
}
}
class Shape extends M.Base {
toJSON () {
const fields = M.fields(this)
let type
// the generated JSON must contain runtime type information for Modélico
// to be able to revive it later
switch (this[M.symbols.typeSymbol]()) {
case Circle:
type = ShapeType.CIRCLE()
break
case Diamond:
type = ShapeType.DIAMOND()
break
default:
throw TypeError('Unsupported Shape in the toJSON method.')
}
return Object.freeze(Object.assign({type}, fields))
}
static innerTypes () {
return Object.freeze({
// notice the self-reference here and how the subclasses are
// going to extend Shape's inner types
relatedShape: maybe(_(Shape))
})
}
static metadata () {
// We are going to use meta so that M.getSchema can give us a more
// robust JSON schema. If you don't need that, you could return the
// baseMetadata directly.
const baseMetadata = Object.assign({}, base(Shape), {reviver})
return meta(baseMetadata, {}, {}, () => ({
anyOf: [
Circle,
Diamond
].map(x => M.getSchema(base(x), false))
}))
}
}Last updated