Skip to content

Instantly share code, notes, and snippets.

@stephenh
Last active August 19, 2021 13:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stephenh/77f62941913203a871d0e284ea779fe9 to your computer and use it in GitHub Desktop.
Save stephenh/77f62941913203a871d0e284ea779fe9 to your computer and use it in GitHub Desktop.
makeSimpleAutoObservable.ts
import { $mobx, isObservable, makeObservable } from "mobx";
const annotationsSymbol = Symbol("annotationsSymbol");
const objectPrototype = Object.prototype;
/**
* A purposefully-limited version of `makeAutoObservable` that supports subclasses.
*
* There is valid complexity in supporting `makeAutoObservable` across disparate/edge-casey
* class hierarchies, and so mobx doesn't support it out of the box. See:
* https://github.com/mobxjs/mobx/discussions/2850#discussioncomment-1203102
*
* So this implementation adds a few limitations that lets us get away with it. Specifically:
*
* - We always auto-infer a key's action/computed/observable, and don't support user-provided config values
* - Subclasses should not override parent class methods (although this might? work)
* - Only the "most child" subclass should call `makeSimpleAutoObservable`, to avoid each constructor in
* the inheritance chain potentially re-decorating keys.
*
* See https://github.com/mobxjs/mobx/discussions/2850
*/
export function makeSimpleAutoObservable(target: any): void {
// These could be params but we hard-code them
const overrides = {} as any;
const options = {};
// Make sure nobody called makeObservable/etc. previously (eg in parent constructor)
if (isObservable(target)) {
throw new Error("Target must not be observable");
}
let annotations = target[annotationsSymbol];
if (!annotations) {
annotations = {};
let current = target;
while (current && current !== objectPrototype) {
Reflect.ownKeys(current).forEach((key) => {
if (key === $mobx || key === "constructor") return;
annotations[key] = !overrides ? true : key in overrides ? overrides[key] : true;
});
current = Object.getPrototypeOf(current);
}
// Cache if class
const proto = Object.getPrototypeOf(target);
if (proto && proto !== objectPrototype) {
Object.defineProperty(proto, annotationsSymbol, { value: annotations });
}
}
return makeObservable(target, annotations, options);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment