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
546 stars 24 forks source link

is a constrained mixin pattern possible? #494

Open krnsk0 opened 1 year ago

krnsk0 commented 1 year ago

I've got a project using mobx-keystone with a complex inheritance hierarchy, and I'm realizing I could simplify things if I switched to an inheritance pattern involving mixins.

The Typescript docs recommend an approach they call "constrained mixins", in which factory functions accept a base class constrained to a type which they extend and return. I'd like to figure out how to do something like this with ExtendedModel.

Here's where I'm at:

// A base class
class Entity extends Model({}) {}   

// An example factory that mixes in some functionality
function makeCountable<T extends ModelClass<Entity>>(Base: T) {
  class _Countable extends ExtendedModel(modelClass(Base), {
    quantity: tProp(types.number, 0)
  }) {

    @modelAction
    increment() {
      this.quantity += 1
    }
  }
  return _Countable
}

// An attempt at describing the Countable type so I can use it
// as a constraint below 
type Countable = ReturnType<typeof makeCountable>

// A second factory which I'd like to be constrained to only accept classes which
// have passed through `makeCountable` already
function makeProducer<T extends ModelClass<Countable>>(Base: T) {
//                                         ^^^^^^^^^
//                                         Type 'typeof _Countable' does not
//                                         satisfy the constraint 'AnyModel
//                                         | AnyDataModel
  class _Producer extends ExtendedModel(modelClass(Base), {
    // etc.
  }) {
    // etc.

  }
  return _Producer
}

Is there a way to make this work? I suspect there's something I should be doing differently specifying the constraint on the type argument to the factories (T Extends ModelClass<etc>). Or it could be that I need some other way of describing the return type of the factories to use as constraints (e.g. type Countable = ReturnType<typeof makeCountable>).

Once I figure out a pattern that works, I'd be happy to contribute it back to the docs on inheritance if this would be useful, generally.

gbcreation commented 1 year ago

I'm also interested by the mixins approach. Did you make any progress on this topic?

krnsk0 commented 1 year ago

I'm also interested by the mixins approach. Did you make any progress on this topic?

I was never able to sort this out, sadly! Using an inheritance hierarchy instead.