Skip to content

Commit

Permalink
feature(api): tell consumers when their objects are observable
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Cottingham committed Sep 13, 2016
1 parent 80495c0 commit b6d04ff
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 8 deletions.
5 changes: 3 additions & 2 deletions src/api/observable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {computed} from "./computeddecorator";
import {isPlainObject, invariant, deprecated} from "../utils/utils";
import {observableDecorator} from "./observabledecorator";
import {isObservable} from "./isobservable";
import {IObservableObject} from "../types/observableobject";
import {IObservableArray, ObservableArray} from "../types/observablearray";

/**
Expand All @@ -15,7 +16,7 @@ export function observable(target: Object, key: string, baseDescriptor?: Propert
export function observable<T>(value: T[]): IObservableArray<T>;
export function observable<T, S extends Object>(value: () => T, thisArg?: S): IObservableValue<T>;
export function observable<T extends string|number|boolean|Date|RegExp|Function|void>(value: T): IObservableValue<T>;
export function observable<T extends Object>(value: T): T;
export function observable<T extends Object>(value: T): T & IObservableObject;
export function observable(v: any = undefined, keyOrScope?: string | any) {
if (typeof arguments[1] === "string")
return observableDecorator.apply(null, arguments);
Expand Down Expand Up @@ -55,4 +56,4 @@ export function getTypeOfValue(value): ValueType {
if (typeof value === "object")
return isPlainObject(value) ? ValueType.PlainObject : ValueType.ComplexObject;
return ValueType.Reference; // safe default, only refer by reference..
}
}
2 changes: 1 addition & 1 deletion src/mobx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export { IComputedValue } from "./core/com
export { asReference, asFlat, asStructure, asMap } from "./types/modifiers";
export { IInterceptable, IInterceptor } from "./types/intercept-utils";
export { IListenable } from "./types/listen-utils";
export { IObjectWillChange, IObjectChange, isObservableObject } from "./types/observableobject";
export { IObjectWillChange, IObjectChange, IObservableObject, isObservableObject } from "./types/observableobject";
export { /* 3.0: IValueDidChange, */ IValueWillChange, IObservableValue } from "./types/observablevalue";

export { IObservableArray, IArrayWillChange, IArrayWillSplice, IArrayChange, IArraySplice, isObservableArray, fastArray } from "./types/observablearray";
Expand Down
10 changes: 7 additions & 3 deletions src/types/observableobject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import {hasInterceptors, IInterceptable, registerInterceptor, interceptChange} f
import {IListenable, registerListener, hasListeners, notifyListeners} from "./listen-utils";
import {isSpyEnabled, spyReportStart, spyReportEnd} from "../core/spy";

export interface IObservableObject {
"observable-object": IObservableObject;
}

// In 3.0, change to IObjectDidChange
export interface IObjectChange {
name: string;
Expand Down Expand Up @@ -198,11 +202,11 @@ function notifyPropertyAddition(adm, object, name: string, newValue) {
spyReportEnd();
}

export function isObservableObject(thing): boolean {
export function isObservableObject<T>(thing: T): thing is T & IObservableObject {
if (typeof thing === "object" && thing !== null) {
// Initializers run lazily when transpiling to babel, so make sure they are run...
runLazyInitializers(thing);
return thing.$mobx instanceof ObservableObjectAdministration;
return (thing as T & {$mobx: any}).$mobx instanceof ObservableObjectAdministration;
}
return false;
}
}
44 changes: 42 additions & 2 deletions test/typescript-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/// <reference path='tape.d.ts' />
import {
observe, computed, observable, asStructure, autorun, autorunAsync, extendObservable, action,
IObservableArray, IArrayChange, IArraySplice, IObservableValue, isObservable, isObservableObject,
IObservableObject, IObservableArray, IArrayChange, IArraySplice, IObservableValue, isObservable, isObservableObject,
extras, Atom, transaction, IObjectChange, spy, useStrict, isAction
} from "../lib/mobx";
import * as test from 'tape';
Expand Down Expand Up @@ -918,4 +918,44 @@ test("373 - fix isObservable for unused computed", t => {

new Bla();
t.end();
})
})

test("484 - observable objects are IObservableObject", t => {
const needs_observable_object = (o: IObservableObject): any => null;
const o = observable({stuff: "things"});

needs_observable_object(o);
t.pass();
t.end();
});

test("484 - observable objects are still type T", t => {
const o = observable({stuff: "things"});
o.stuff = "new things";
t.pass();
t.end();
});

test("484 - isObservableObject type guard includes type T", t => {
const o = observable({stuff: "things"});
if(isObservableObject(o)) {
o.stuff = "new things";
t.pass();
} else {
t.fail("object should have been observable");
}
t.end();
});

test("484 - isObservableObject type guard includes type IObservableObject", t => {
const requires_observable_object = (o: IObservableObject): void => null;
const o = observable({stuff: "things"});

if(isObservableObject(o)) {
requires_observable_object(o);
t.pass();
} else {
t.fail("object should have been IObservableObject");
}
t.end();
});

0 comments on commit b6d04ff

Please sign in to comment.