From 85ffdf2e400fe8050837295c580393f9488d6fba Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Wed, 7 Dec 2016 08:45:16 +0100 Subject: [PATCH] Add warning about automatic computed conversion, see #421 / #532 --- src/types/observableobject.ts | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/types/observableobject.ts b/src/types/observableobject.ts index 1af19f87a..fd64c5785 100644 --- a/src/types/observableobject.ts +++ b/src/types/observableobject.ts @@ -2,12 +2,20 @@ import {ObservableValue, UNCHANGED} from "./observablevalue"; import {isComputedValue, ComputedValue} from "../core/computedvalue"; import {isAction} from "../api/action"; import {ValueMode, getModifier} from "./modifiers"; -import {createInstanceofPredicate, isObject, Lambda, getNextId, invariant, assertPropertyConfigurable, isPlainObject, addHiddenFinalProp} from "../utils/utils"; +import {createInstanceofPredicate, isObject, Lambda, getNextId, invariant, assertPropertyConfigurable, isPlainObject, addHiddenFinalProp, deprecated} from "../utils/utils"; import {runLazyInitializers} from "../utils/decorators"; import {hasInterceptors, IInterceptable, registerInterceptor, interceptChange} from "./intercept-utils"; import {IListenable, registerListener, hasListeners, notifyListeners} from "./listen-utils"; import {isSpyEnabled, spyReportStart, spyReportEnd} from "../core/spy"; +const COMPUTED_FUNC_DEPRECATED = ( +` +In MobX 2.* passing a function without arguments to (extend)observable will automatically be inferred to be a computed value. +This behavior is ambiguous and will change in MobX 3 to create just an observable reference to the value passed in. +To disambiguate, please pass the function wrapped with a modifier: use 'computed(fn)' (for current behavior; automatic conversion), or 'asReference(fn)' (future behavior, just store reference) or 'action(fn)'. +Note that the idiomatic way to write computed properties is 'observable({ get propertyName() { ... }})'. +For more details, see https://github.com/mobxjs/mobx/issues/532`); + export interface IObservableObject { "observable-object": IObservableObject; } @@ -60,7 +68,7 @@ export function asObservableObject(target, name: string, mode: ValueMode = Value return target.$mobx; if (!isPlainObject(target)) - name = target.constructor.name + "@" + getNextId(); + name = (target.constructor.name || "ObservableObject") + "@" + getNextId(); if (!name) name = "ObservableObject@" + getNextId(); @@ -69,15 +77,24 @@ export function asObservableObject(target, name: string, mode: ValueMode = Value return adm; } +function handleAsComputedValue(value): boolean { + return typeof value === "function" && value.length === 0 && !isAction(value) +} + export function setObservableObjectInstanceProperty(adm: ObservableObjectAdministration, propName: string, descriptor: PropertyDescriptor) { if (adm.values[propName]) { invariant("value" in descriptor, "cannot redefine property " + propName); adm.target[propName] = descriptor.value; // the property setter will make 'value' reactive if needed. } else { - if ("value" in descriptor) + if ("value" in descriptor) { + if (handleAsComputedValue(descriptor.value)) { + // warn about automatic inference, see https://github.com/mobxjs/mobx/issues/421 + deprecated(`${COMPUTED_FUNC_DEPRECATED})in: ${adm.name}.${propName}`); + } defineObservableProperty(adm, propName, descriptor.value, true, undefined); - else + } else { defineObservableProperty(adm, propName, descriptor.get, true, descriptor.set); + } } } @@ -90,15 +107,14 @@ export function defineObservableProperty(adm: ObservableObjectAdministration, pr let isComputed = true; if (isComputedValue(newValue)) { - // desugger computed(getter, setter) - // TODO: deprecate this and remove in 3.0, to keep them boxed + // desugar computed(getter, setter) + // TODO: deprecate this and remove in 3.0, to keep them boxed? // get / set is now the idiomatic syntax for non-boxed computed values observable = newValue; newValue.name = name; if (!newValue.scope) newValue.scope = adm.target; - } else if (typeof newValue === "function" && newValue.length === 0 && !isAction(newValue)) { - // TODO: add warning in 2.6, see https://github.com/mobxjs/mobx/issues/421 + } else if (handleAsComputedValue(newValue)) { // TODO: remove in 3.0 observable = new ComputedValue(newValue, adm.target, false, name, setter); } else if (getModifier(newValue) === ValueMode.Structure && typeof newValue.value === "function" && newValue.value.length === 0) {