diff --git a/src/types/modifiers.ts b/src/types/modifiers.ts index 5b14675f1..1cc5665b4 100644 --- a/src/types/modifiers.ts +++ b/src/types/modifiers.ts @@ -1,4 +1,4 @@ -import {isPlainObject, invariant} from "../utils/utils"; +import {isPlainObject, invariant, isObject} from "../utils/utils"; import {isObservable} from "../api/isobservable"; import {extendObservableHelper} from "../api/extendobservable"; import {createObservableArray} from "../types/observablearray"; @@ -12,6 +12,26 @@ export enum ValueMode { Flat // If the value is an plain object, it will be made reactive, and so will all its future children. } +export interface IModifierWrapper { + mobxModifier: ValueMode; + value: any; +} + +function withModifier(modifier: ValueMode, value: any): IModifierWrapper { + assertUnwrapped(value, "Modifiers are not allowed to be nested"); + return { + mobxModifier: modifier, + value + }; +} + +export function getModifier(value: any): ValueMode { + if (value) { // works for both objects and functions + return (value.mobxModifier as ValueMode) || null; + } + return null; +} + /** * Can be used in combination with makeReactive / extendReactive. @@ -23,8 +43,9 @@ export enum ValueMode { export function asReference(value: T): T { // unsound typecast, but in combination with makeReactive, the end result should be of the correct type this way // e.g: makeReactive({ x : asReference(number)}) -> { x : number } - return new AsReference(value); + return withModifier(ValueMode.Reference, value) as any as T; } +(asReference as any).mobxModifier = ValueMode.Reference; /** * Can be used in combination with makeReactive / extendReactive. @@ -37,8 +58,9 @@ export function asReference(value: T): T { * @param value initial value of the reactive property that is being defined. */ export function asStructure(value: T): T { - return new AsStructure(value); + return withModifier(ValueMode.Structure, value) as any as T; } +(asStructure as any).mobxModifier = ValueMode.Structure; /** * Can be used in combination with makeReactive / extendReactive. @@ -46,26 +68,9 @@ export function asStructure(value: T): T { * children will not automatically be made reactive as well. */ export function asFlat(value: T): T { - return new AsFlat(value); -} - -export class AsReference { - constructor(public value: any) { - assertUnwrapped(value, "Modifiers are not allowed to be nested"); - } -} - -export class AsStructure { - constructor(public value: any) { - assertUnwrapped(value, "Modifiers are not allowed to be nested"); - } -} - -export class AsFlat { - constructor(public value: any) { - assertUnwrapped(value, "Modifiers are not allowed to be nested"); - } + return withModifier(ValueMode.Flat, value) as any as T; } +(asFlat as any).mobxModifier = ValueMode.Flat; export function asMap(): ObservableMap; export function asMap(): ObservableMap; @@ -76,24 +81,18 @@ export function asMap(data?, modifierFunc?): ObservableMap { } export function getValueModeFromValue(value: any, defaultMode: ValueMode): [ValueMode, any] { - if (value instanceof AsReference) - return [ValueMode.Reference, value.value]; - if (value instanceof AsStructure) - return [ValueMode.Structure, value.value]; - if (value instanceof AsFlat) - return [ValueMode.Flat, value.value]; + const mode = getModifier(value); + if (mode) + return [mode, value.value]; return [defaultMode, value]; } export function getValueModeFromModifierFunc(func?: Function): ValueMode { - if (func === asReference) - return ValueMode.Reference; - else if (func === asStructure) - return ValueMode.Structure; - else if (func === asFlat) - return ValueMode.Flat; - invariant(func === undefined, "Cannot determine value mode from function. Please pass in one of these: mobx.asReference, mobx.asStructure or mobx.asFlat, got: " + func); - return ValueMode.Recursive; + if (func === undefined) + return ValueMode.Recursive; + const mod = getModifier(func); + invariant(mod !== null, "Cannot determine value mode from function. Please pass in one of these: mobx.asReference, mobx.asStructure or mobx.asFlat, got: " + func); + return mod; } @@ -128,6 +127,6 @@ export function makeChildObservable(value, parentMode: ValueMode, name?: string) } export function assertUnwrapped(value, message) { - if (value instanceof AsReference || value instanceof AsStructure || value instanceof AsFlat) + if (getModifier(value) !== null) throw new Error(`[mobx] asStructure / asReference / asFlat cannot be used here. ${message}`); } diff --git a/src/types/observableobject.ts b/src/types/observableobject.ts index 05ec108ab..1af19f87a 100644 --- a/src/types/observableobject.ts +++ b/src/types/observableobject.ts @@ -1,7 +1,7 @@ -import {isObservableValue, ObservableValue, UNCHANGED} from "./observablevalue"; +import {ObservableValue, UNCHANGED} from "./observablevalue"; import {isComputedValue, ComputedValue} from "../core/computedvalue"; import {isAction} from "../api/action"; -import {ValueMode, AsStructure} from "./modifiers"; +import {ValueMode, getModifier} from "./modifiers"; import {createInstanceofPredicate, isObject, Lambda, getNextId, invariant, assertPropertyConfigurable, isPlainObject, addHiddenFinalProp} from "../utils/utils"; import {runLazyInitializers} from "../utils/decorators"; import {hasInterceptors, IInterceptable, registerInterceptor, interceptChange} from "./intercept-utils"; @@ -101,7 +101,7 @@ export function defineObservableProperty(adm: ObservableObjectAdministration, pr // TODO: add warning in 2.6, see https://github.com/mobxjs/mobx/issues/421 // TODO: remove in 3.0 observable = new ComputedValue(newValue, adm.target, false, name, setter); - } else if (newValue instanceof AsStructure && typeof newValue.value === "function" && newValue.value.length === 0) { + } else if (getModifier(newValue) === ValueMode.Structure && typeof newValue.value === "function" && newValue.value.length === 0) { observable = new ComputedValue(newValue.value, adm.target, true, name, setter); } else { isComputed = false; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index c1b3162d4..33003deb8 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -167,6 +167,7 @@ export function deepEquals(a, b) { } export function createInstanceofPredicate(name: string, clazz: new (...args:any[]) => T): (x: any) => x is T { + // TODO: this is quite a slow aproach, find something faster? const propName = "isMobX" + name; clazz.prototype[propName] = true; return function (x) {