diff --git a/.changeset/observable-map-get-or-insert.md b/.changeset/observable-map-get-or-insert.md new file mode 100644 index 000000000..d8e72c110 --- /dev/null +++ b/.changeset/observable-map-get-or-insert.md @@ -0,0 +1,5 @@ +--- +"mobx": patch +--- + +Add `getOrInsert` and `getOrInsertComputed` to `ObservableMap` for compatibility with ESNext `Map` typings. diff --git a/packages/mobx/__tests__/v5/base/map.js b/packages/mobx/__tests__/v5/base/map.js index c21b5b11d..31507849b 100644 --- a/packages/mobx/__tests__/v5/base/map.js +++ b/packages/mobx/__tests__/v5/base/map.js @@ -700,6 +700,38 @@ test("issue 940, should not be possible to change maps outside strict mode", () } }) +test("map getOrInsert", function () { + const names = map({ user1: "User 1" }) + + const values = [] + const dispose = autorun(() => { + values.push(names.getOrInsert("user1", "User 2")) + }) + names.set("user1", "Updated") + dispose() + expect(values).toEqual(["User 1", "Updated"]) + + expect(names.getOrInsert("user2", "User 2")).toBe("User 2") + expect(names.get("user2")).toBe("User 2") +}) + +test("map getOrInsertComputed", function () { + const names = map({ user1: "User 1" }) + const getName = userId => "User " + userId.slice(4) + + const values = [] + const dispose = autorun(() => { + values.push(names.getOrInsertComputed("user1", getName)) + }) + names.set("user1", "Updated") + dispose() + + expect(values).toEqual(["User 1", "Updated"]) + + expect(names.getOrInsertComputed("user2", getName)).toBe("User 2") + expect(names.get("user2")).toBe("User 2") +}) + test("issue 1243, .replace should not trigger change on unchanged values", () => { const m = mobx.observable.map({ a: 1, b: 2, c: 3 }) diff --git a/packages/mobx/src/types/observablemap.ts b/packages/mobx/src/types/observablemap.ts index 014b65507..15061103a 100644 --- a/packages/mobx/src/types/observablemap.ts +++ b/packages/mobx/src/types/observablemap.ts @@ -289,6 +289,20 @@ export class ObservableMap return this.dehanceValue_(undefined) } + getOrInsert(key: K, value: V): V { + if (!this.has(key)) { + this.set(key, value) + } + return this.get(key)! + } + + getOrInsertComputed(key: K, callback: (key: K) => V): V { + if (!this.has(key)) { + this.set(key, callback(key)) + } + return this.get(key)! + } + private dehanceValue_(value: X): X { if (this.dehancer !== undefined) { return this.dehancer(value)