xaviergonz / mobx-keystone

A MobX powered state management solution based on data trees with first class support for Typescript, support for snapshots, patches and much more
https://mobx-keystone.js.org
MIT License
549 stars 25 forks source link

Model factory pattern: ModelProps type issues #11

Closed terrysahaidak closed 5 years ago

terrysahaidak commented 5 years ago

I'm trying to migrate my entities store pattern into mobx-keystone with typescript and it's pretty hard for me since my ts isn't really good.

Here is my entities store factory: https://t.co/dKnklXwgnF

Here is usage: https://t.co/4kpAzmAxCZ

It receives just a collection store I defined here: https://t.co/M0JNTErYQL

The problem with that part. It creates a map with all my collection stores and passes it as model props: https://t.co/uSqmius1zF

But seems like that part isn't working since ts doesn't resolve todos prop of my entities store which is one of my collection stores: https://t.co/rImQry1YES

Maybe you have some suggestions on how to trick it?

xaviergonz commented 5 years ago

I took a look, the problem seems to lie in the createEntitiesStore typing, where it downgrades the argument property to a generic ModelProps (which has no actual property names), but TBH I couldn't find a way to make it work as is. Have you considered using an ObjectMap (or something similar) inside maybe a custom model class as entity store?

terrysahaidak commented 5 years ago

Managed to get it working by doing something like that:

export function createEntitiesStore(
  collectionStores: ModelProps = {},
) {
  class EntitiesStore extends Model(collectionStores) {
    @modelAction merge(normalizedEntities: { [k: string]: Object }) {}
      normalizeMerge(item: any, schema: Schema) {}
  }

  return EntitiesStore;
}

@model('EntitiesStore')
export class EntitiesStore extends ExtendedModel(
  createEntitiesStore(),
  {
    todos: prop<ITodoCollectionStore>(
      () => new TodoCollectionStore({}),
    ),
  },
) {
  // you can add all the async actions here
}

How the ModalProps works? If it does not have all the props you're passing to it, how it knows what did I pass then? If I'm passing all my collection stores using createEntitiesStore({}), it doesn't work.

terrysahaidak commented 5 years ago

Ok, I've just got it working, will leave here, maybe will be useful for somebody

// definition
export function EntitiesModel<
  TBaseProps extends {
    [k: string]: ModelProp<any, any>;
  }
>(collectionStores: TBaseProps) {
  class EntitiesStore extends Model(collectionStores as ModelProps) {
    // some additional actions/computed
  }

  return ExtendedModel(EntitiesStore, collectionStores);
}

// actual store:

@model('EntitiesStore')
export class EntitiesStore extends EntitiesModel({
  todos: prop<ITodoCollectionStore>(
    () => new TodoCollectionStore({}),
  ),
  groups: prop<IGroupCollectionStore>(
    () => new GroupCollectionStore({}),
  ),
}) {}

export type IEntityStore = InstanceType<typeof EntitiesStore>;

const store = new EntitiesStore({});
store.todos // exists

By the way, would be cool to have ModelProps and ModelProp available as public interfaces :)

xaviergonz commented 5 years ago

Hey, I'm really glad you got it working! Just wondering, are you really using the IEntityStore type? You should be able to just use EntitiesStore directly as type since the (final) class doesn't come from a factory

xaviergonz commented 5 years ago

Oh, and just pushed a commit to make those public, so they will be in the next version :)

terrysahaidak commented 5 years ago

@xaviergonz you're right. thanks, haven't changed it since I've used a different approach :)

Thanks for your effort.

(don't know why I'm keeping writing modal instead of model, every time)