Repository: immerjs/immer
Pull request #767 (2021-03-18 10:22:47)

fix isPlainObject to support cross frames
https://github.com/immerjs/immer/issues/766


Patch:
diff --git a/package.json b/package.json
index b552336..21a969c 100644
--- a/package.json
+++ b/package.json
@@ -34,7 +34,7 @@
     "coveralls": "jest --coverage && cat ./coverage/lcov.info | ./node_modules/.bin/coveralls && rm -rf ./coverage",
     "build": "rimraf dist/ && tsdx build --name immer --format esm,cjs,umd && yarn build:flow",
     "build:flow": "cpx 'src/types/index.js.flow' dist -v",
-    "publish-docs": "cd website && GIT_USER=mweststrate USE_SSH=true yarn run publish-gh-pages",
+    "publish-docs": "cd website && GIT_USER=mweststrate USE_SSH=true yarn docusaurus deploy",
     "start": "cd website && yarn start",
     "test:size": "yarn build && yarn import-size --report . produce enableES5 enableMapSet enablePatches enableAllPlugins",
     "test:sizequick": "tsdx build --name immer --format esm && yarn import-size . produce"
diff --git a/src/core/immerClass.ts b/src/core/immerClass.ts
index f98d375..1818e90 100644
--- a/src/core/immerClass.ts
+++ b/src/core/immerClass.ts
@@ -42,8 +42,6 @@ export class Immer implements ProducersFns {
 			this.setUseProxies(config!.useProxies)
 		if (typeof config?.autoFreeze === "boolean")
 			this.setAutoFreeze(config!.autoFreeze)
-		this.produce = this.produce.bind(this)
-		this.produceWithPatches = this.produceWithPatches.bind(this)
 	}
 
 	/**
@@ -65,7 +63,7 @@ export class Immer implements ProducersFns {
 	 * @param {Function} patchListener - optional function that will be called with all the patches produced here
 	 * @returns {any} a new state, or the initial state if nothing was modified
 	 */
-	produce(base: any, recipe?: any, patchListener?: any) {
+	produce: IProduce = (base: any, recipe?: any, patchListener?: any) => {
 		// curried invocation
 		if (typeof base === "function" && typeof recipe !== "function") {
 			const defaultBase = recipe
@@ -123,7 +121,11 @@ export class Immer implements ProducersFns {
 		} else die(21, base)
 	}
 
-	produceWithPatches(arg1: any, arg2?: any, arg3?: any): any {
+	produceWithPatches: IProduceWithPatches = (
+		arg1: any,
+		arg2?: any,
+		arg3?: any
+	): any => {
 		if (typeof arg1 === "function") {
 			return (state: any, ...args: any[]) =>
 				this.produceWithPatches(state, (draft: any) => arg1(draft, ...args))
diff --git a/src/utils/common.ts b/src/utils/common.ts
index 16f9430..58de2b6 100644
--- a/src/utils/common.ts
+++ b/src/utils/common.ts
@@ -36,11 +36,20 @@ export function isDraftable(value: any): boolean {
 	)
 }
 
+const objectCtorString = Object.prototype.constructor.toString()
 /*#__PURE__*/
 export function isPlainObject(value: any): boolean {
 	if (!value || typeof value !== "object") return false
 	const proto = Object.getPrototypeOf(value)
-	return !proto || proto === Object.prototype
+	if (proto === null) {
+		return true
+	}
+	const Ctor =
+		Object.hasOwnProperty.call(proto, "constructor") && proto.constructor
+	return (
+		typeof Ctor == "function" &&
+		Function.toString.call(Ctor) === objectCtorString
+	)
 }
 
 /** Get the underlying object that is represented by the given draft */


Test patch:
diff --git a/__tests__/produce.ts b/__tests__/produce.ts
index c927e1a..c0fb74e 100644
--- a/__tests__/produce.ts
+++ b/__tests__/produce.ts
@@ -6,7 +6,8 @@ import produce, {
 	nothing,
 	Draft,
 	Immutable,
-	enableAllPlugins
+	enableAllPlugins,
+	Immer
 } from "../src/immer"
 
 enableAllPlugins()
@@ -477,3 +478,19 @@ it("shows error in production if called incorrectly", () => {
 			: "[Immer] The first or second argument to `produce` must be a function"
 	)
 })
+
+it("#749 types Immer", () => {
+	const t = {
+		x: 3
+	}
+
+	const immer = new Immer()
+	const z = immer.produce(t, d => {
+		d.x++
+		// @ts-expect-error
+		d.y = 0
+	})
+	expect(z.x).toBe(4)
+	// @ts-expect-error
+	expect(z.z).toBeUndefined()
+})