Last active
August 19, 2021 13:30
-
-
Save stephenh/77f62941913203a871d0e284ea779fe9 to your computer and use it in GitHub Desktop.
makeSimpleAutoObservable.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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