Repository: pmndrs/zustand
Pull request #1459 (2022-12-03 17:47:34)

refactor(vanilla/types): replace hardcoded types with StoreApi type
Add binding of createStoreImpl methods types to StoreApi without hardcoding.

## Related Issues
In case of changing something in StoreApi type we dont have to change it again inside createStoreImpl

Fixes #.

## Summary
Removing arrow function types reduces number of arrows, and makes it easier to read.
 Makes it easier to read and reason about where the type comes from and belongs to. 



## Check List

- [x] `yarn run prettier` for formatting code and docs


codesandbox-ci[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/pmndrs/zustand/pr/1459/builds/322667) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit b07d80dcc6e490bf337068496b0283d466f037c1:

|Sandbox| Source |
|--|--|
|[React](https://codesandbox.io/s/react-mj1obt)| Configuration |
|[React Typescript](https://codesandbox.io/s/react-typescript-4ctdpu)| Configuration |
|[React Browserify](https://codesandbox.io/s/react-browserify-qgmrnp)| Configuration |
|[React Snowpack](https://codesandbox.io/s/react-snowpack-v69vq0)| Configuration |
|[React Parcel](https://codesandbox.io/s/react-parcel-uozfsc)| Configuration |
|[Next.js](https://codesandbox.io/s/next-js-148uzr)| Configuration |

devanshj: Looks fine to me too.

Patch:
diff --git a/docs/guides/auto-generating-selectors.md b/docs/guides/auto-generating-selectors.md
index 27f5320..fe30662 100644
--- a/docs/guides/auto-generating-selectors.md
+++ b/docs/guides/auto-generating-selectors.md
@@ -14,13 +14,13 @@ However, writing these could be tedious. If that is the case for you, you can au
 ## create the following function: `createSelectors`
 
 ```typescript
-import { State, StoreApi, UseBoundStore } from 'zustand'
+import { StoreApi, UseBoundStore } from 'zustand'
 
 type WithSelectors<S> = S extends { getState: () => infer T }
   ? S & { use: { [K in keyof T]: () => T[K] } }
   : never
 
-const createSelectors = <S extends UseBoundStore<StoreApi<State>>>(
+const createSelectors = <S extends UseBoundStore<StoreApi<object>>>(
   _store: S
 ) => {
   let store = _store as WithSelectors<typeof _store>
diff --git a/docs/integrations/3rd-party-libraries.md b/docs/integrations/3rd-party-libraries.md
index 589437f..e9a3565 100644
--- a/docs/integrations/3rd-party-libraries.md
+++ b/docs/integrations/3rd-party-libraries.md
@@ -11,11 +11,13 @@ Zustand provides bear necessities for state management which is great for most p
 - [@dhmk/zustand-lens](https://github.com/dhmk083/dhmk-zustand-lens) - Lens support for zustand
 - [@liveblocks/zustand](https://github.com/liveblocks/liveblocks/tree/main/packages/liveblocks-zustand) - Liveblocks middleware to make your application multiplayer.
 - [auto-zustand-selectors-hook](https://github.com/Albert-Gao/auto-zustand-selectors-hook) - Auto generate zustand hooks for you store with Typescript support
+- [derive-zustand](https://github.com/dai-shi/derive-zustand) - A function to create a derived Zustand store from stores
 - [geschichte](https://github.com/BowlingX/geschichte) - zustand and immer based hook to manage query parameters
 - [mobz](https://github.com/2A5F/Mobz) - zustand style mobx api
 - [shared-zustand](https://github.com/Tom-Julux/shared-zustand) - cross-tab state sharing for zustand
 - [simple-zustand-devtools](https://github.com/beerose/simple-zustand-devtools) - Inspect your zustand store in React DevTools 🐻⚛️
 - [solid-zustand](https://github.com/wobsoriano/solid-zustand) - Zustand state management for Solid.
+- [use-zustand](https://github.com/dai-shi/use-zustand) - Another custom hook to use Zustand vanilla store
 - [vue-zustand](https://github.com/wobsoriano/vue-zustand) - State-management solution for Vue based on Zustand.
 - [zoov](https://github.com/InfiniteXyy/zoov) - Use 🐻 Zustand with Module-like api
 - [zundo](https://github.com/charkour/zundo) - 🍜 enable time-travel in your apps. undo/redo middleware for zustand
diff --git a/src/middleware/devtools.ts b/src/middleware/devtools.ts
index 1c6641d..6f958e5 100644
--- a/src/middleware/devtools.ts
+++ b/src/middleware/devtools.ts
@@ -160,7 +160,7 @@ const devtoolsImpl: DevtoolsImpl =
     try {
       extensionConnector =
         (enabled ?? __DEV__) && window.__REDUX_DEVTOOLS_EXTENSION__
-    } catch {
+    } catch (e) {
       // ignored
     }
 
diff --git a/src/shallow.ts b/src/shallow.ts
index 6d9ec7c..0e7f59e 100644
--- a/src/shallow.ts
+++ b/src/shallow.ts
@@ -10,6 +10,29 @@ function shallow<T>(objA: T, objB: T) {
   ) {
     return false
   }
+
+  if (objA instanceof Map && objB instanceof Map) {
+    if (objA.size !== objB.size) return false
+
+    for (const [key, value] of objA) {
+      if (!Object.is(value, objB.get(key))) {
+        return false
+      }
+    }
+    return true
+  }
+
+  if (objA instanceof Set && objB instanceof Set) {
+    if (objA.size !== objB.size) return false
+
+    for (const value of objA) {
+      if (!objB.has(value)) {
+        return false
+      }
+    }
+    return true
+  }
+
   const keysA = Object.keys(objA)
   if (keysA.length !== Object.keys(objB).length) {
     return false
diff --git a/src/vanilla.ts b/src/vanilla.ts
index 315b07c..469ac70 100644
--- a/src/vanilla.ts
+++ b/src/vanilla.ts
@@ -60,7 +60,7 @@ const createStoreImpl: CreateStoreImpl = (createState) => {
   let state: TState
   const listeners: Set<Listener> = new Set()
 
-  const setState: SetStateInternal<TState> = (partial, replace) => {
+  const setState: StoreApi<TState>['setState'] = (partial, replace) => {
     // TODO: Remove type assertion once https://github.com/microsoft/TypeScript/issues/37663 is resolved
     // https://github.com/microsoft/TypeScript/issues/37663#issuecomment-759728342
     const nextState =
@@ -77,15 +77,15 @@ const createStoreImpl: CreateStoreImpl = (createState) => {
     }
   }
 
-  const getState: () => TState = () => state
+  const getState: StoreApi<TState>['getState'] = () => state
 
-  const subscribe: (listener: Listener) => () => void = (listener) => {
+  const subscribe: StoreApi<TState>['subscribe'] = (listener) => {
     listeners.add(listener)
     // Unsubscribe
     return () => listeners.delete(listener)
   }
 
-  const destroy: () => void = () => listeners.clear()
+  const destroy: StoreApi<TState>['destroy'] = () => listeners.clear()
   const api = { setState, getState, subscribe, destroy }
   state = createState(setState, getState, api)
   return api as any


Test patch:
diff --git a/tests/shallow.test.tsx b/tests/shallow.test.tsx
index 681b217..a60008e 100644
--- a/tests/shallow.test.tsx
+++ b/tests/shallow.test.tsx
@@ -39,6 +39,43 @@ describe('shallow', () => {
     expect(shallow([{ foo: 'bar' }], [{ foo: 'bar', asd: 123 }])).toBe(false)
   })
 
+  it('compares Maps', () => {
+    function createMap<T extends object>(obj: T) {
+      return new Map(Object.entries(obj))
+    }
+
+    expect(
+      shallow(
+        createMap({ foo: 'bar', asd: 123 }),
+        createMap({ foo: 'bar', asd: 123 })
+      )
+    ).toBe(true)
+
+    expect(
+      shallow(
+        createMap({ foo: 'bar', asd: 123 }),
+        createMap({ foo: 'bar', foobar: true })
+      )
+    ).toBe(false)
+
+    expect(
+      shallow(
+        createMap({ foo: 'bar', asd: 123 }),
+        createMap({ foo: 'bar', asd: 123, foobar: true })
+      )
+    ).toBe(false)
+  })
+
+  it('compares Sets', () => {
+    expect(shallow(new Set(['bar', 123]), new Set(['bar', 123]))).toBe(true)
+
+    expect(shallow(new Set(['bar', 123]), new Set(['bar', 2]))).toBe(false)
+
+    expect(shallow(new Set(['bar', 123]), new Set(['bar', 123, true]))).toBe(
+      false
+    )
+  })
+
   it('compares functions', () => {
     function firstFnCompare() {
       return { foo: 'bar' }