Skip to content

Commit

Permalink
modifiers no longer use classes / instanceof
Browse files Browse the repository at this point in the history
  • Loading branch information
mweststrate committed Sep 29, 2016
1 parent a270fda commit 8471ffb
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 40 deletions.
73 changes: 36 additions & 37 deletions src/types/modifiers.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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.
Expand All @@ -23,8 +43,9 @@ export enum ValueMode {
export function asReference<T>(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 <T><any> 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.
Expand All @@ -37,35 +58,19 @@ export function asReference<T>(value: T): T {
* @param value initial value of the reactive property that is being defined.
*/
export function asStructure<T>(value: T): T {
return <T><any>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.
* The value will be made reactive, but, if the value is an object or array,
* children will not automatically be made reactive as well.
*/
export function asFlat<T>(value: T): T {
return <T><any> 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<any>;
export function asMap<T>(): ObservableMap<T>;
Expand All @@ -76,24 +81,18 @@ export function asMap(data?, modifierFunc?): ObservableMap<any> {
}

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;
}


Expand Down Expand Up @@ -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}`);
}
6 changes: 3 additions & 3 deletions src/types/observableobject.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export function deepEquals(a, b) {
}

export function createInstanceofPredicate<T>(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) {
Expand Down

0 comments on commit 8471ffb

Please sign in to comment.