Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extending a model in mobx state tree

I have a bunch of stores each containing a list of one entity type like

const userStore = EntityStore.create(....)

const supplierStore = EntityStore.create(....)

Some stores can offer additional functionalities, so I wrote

const orderStore = EntityStore
.views(self => ({
    allByUserId: branchId => ....)
}))
.create(....)

So far, everything was fine, but now I wanted to create a "store manager" containing a list of all such stores and it failed with a message like

Error: [mobx-state-tree] Error while converting ...
value of type EntityStore: (id: Order)> is not assignable to type: EntityStore,
expected an instance of EntityStore or a snapshot like ... instead
(Note that a snapshot of the provided value is compatible with the targeted type)

The message is clear, my "EntityStore with views" is not of the same type as an "EntityStore". But it's an extension of it, so I wonder if there's a declaration allowing it. Something like List<? extends EntityStore> in Java?

Or a nice workaround allowing me to add the additional functionality to EntityStore without changing its type?

like image 879
maaartinus Avatar asked Feb 07 '19 05:02

maaartinus


1 Answers

No. You can't. Because .views() (as basically any other dot method) creates a whole new ModelType object every time you invoke it.

What you could do instead is use a union type:

  • types.union(options?: { dispatcher?: (snapshot) => Type, eager?: boolean }, types...) create a union of multiple types. If the correct type cannot be inferred unambiguously from a snapshot, provide a dispatcher function to determine the type. When eager flag is set to true (default) - the first matching type will be used, if set to false the type check will pass only if exactly 1 type matches.

There's also an example below of how to simulate inheritance by using type composition:

const Square = types
    .model(
        "Square",
        {
            width: types.number
        }
    )
    .views(self => ({
        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
        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)

So, no inheritance, but, union types and code reuse.

like image 158
jayarjo Avatar answered Nov 09 '22 19:11

jayarjo