Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Make observable map structurally match an ES6 Map #1361

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion src/types/observablemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
deprecated,
isES6Map,
getMapLikeKeys,
fail
fail,
declareStringTag
} from "../utils/utils"
import {
IInterceptable,
Expand Down Expand Up @@ -40,9 +41,18 @@ export interface IMap<K, V> {
get(key: K): V | undefined
has(key: K): boolean
set(key: K, value?: V): this
[Symbol.toStringTag]: "Map"
[Symbol.iterator](): IterableIterator<[K, V]>
entries(): IterableIterator<[K, V]>
keys(): IterableIterator<K>
values(): IterableIterator<V>
readonly size: number
}

export interface IterableIterator<T> extends Iterator<T> {
[Symbol.iterator](): IterableIterator<T>
}

export interface IKeyValueMap<V> {
[key: string]: V
}
Expand Down Expand Up @@ -376,10 +386,20 @@ export class ObservableMap<V>
}
}

/**
* Include the interface to cover the implementations added to the prototype directly.
*/
export interface ObservableMap<V> {
[Symbol.toStringTag]: "Map"
[Symbol.iterator](): IterableIterator<[string, V]>
}

declareIterator(ObservableMap.prototype, function() {
return this.entries()
})

declareStringTag(ObservableMap.prototype, "Map")

export function map<V>(initialValues?: IObservableMapInitialValues<V>): ObservableMap<V> {
deprecated("`mobx.map` is deprecated, use `new ObservableMap` or `mobx.observable.map` instead")
return observable.map<V>(initialValues)
Expand Down
12 changes: 8 additions & 4 deletions src/utils/iterable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ function iteratorSymbol() {

export const IS_ITERATING_MARKER = "__$$iterating"

export interface IteratorResult<T> {
done: boolean
value: T
}

export interface Iterator<T> {
next(): {
done: boolean
value?: T
}
next(value?: any): IteratorResult<T>
return?(value?: any): IteratorResult<T>
throw?(e?: any): IteratorResult<T>
}

export function arrayAsIterator<T>(array: T[]): T[] & Iterator<T> {
Expand Down
6 changes: 6 additions & 0 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ export function toPrimitive(value) {
return value === null ? null : typeof value === "object" ? "" + value : value
}

const stringTagSymbol = (typeof Symbol === "function" && Symbol.toStringTag) || "@@toStringTag"

export function declareStringTag<T>(prototType, tag: string) {
addHiddenFinalProp(prototType, stringTagSymbol, tag)
}

import { globalState } from "../core/globalstate"
import { IObservableArray, isObservableArray } from "../types/observablearray"
import { isObservableMap, ObservableMap, IKeyValueMap } from "../types/observablemap"
Expand Down
6 changes: 6 additions & 0 deletions test/base/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -642,3 +642,9 @@ test("#1258 cannot replace maps anymore", () => {
const items = mobx.observable.map()
items.replace(mobx.observable.map())
})

test("map should implement toStringTag", () => {
const m = mobx.observable.map({ a: 1 })
const name = Object.prototype.toString.call(m)
expect(name).toBe("[object Map]")
})
5 changes: 5 additions & 0 deletions test/base/typescript-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1247,3 +1247,8 @@ test("1072 - @observable without initial value and observe before first access",
const user = new User()
observe(user, "loginCount", () => {})
})

test("map should structurally match ES6 Map", () => {
// Including this line strictly for type checking.
const m: Map<string, number> = mobx.observable.map({ a: 1, b: 2 })
})
5 changes: 4 additions & 1 deletion test/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"target": "es5",
"noImplicitAny": true,
"sourceMap": false,
"experimentalDecorators": true
"experimentalDecorators": true,
"lib": [
"es2015"
]
}
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"noImplicitAny": false,
"moduleResolution": "node",
"lib": [
"es5",
"es2015",
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to change this so I could use Symbol.toStringMap and Symbol.iterator in the typings.

"dom"
]
},
Expand Down