chrisvander / zustand-computed

A Zustand middleware to create computed states.
MIT License
81 stars 8 forks source link

I'm using a combination of immer middleware and typescript, but my computedState type doesn't exist! #24

Open LiuCaptain opened 1 month ago

LiuCaptain commented 1 month ago

Here is my code:

import { StoreApi, useStore } from 'zustand';
import { createStore } from 'zustand/vanilla';
import { devtools, persist, subscribeWithSelector } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import computed from 'zustand-computed';
import baseSlice from './baseSlice';
import userInfoSlice, { userInfoComputedState } from './userInfoSlice';
import type { BaseSlice } from './baseSlice';
import type { UserInfoSlice, UserInfoComputedState } from './userInfoSlice';

interface RootSlice {
  _hasHydrated: boolean;
  setHasHydrated: (state: boolean) => void;
}

type ComputedState = UserInfoComputedState;

export type BoundStore = RootSlice & BaseSlice & UserInfoSlice;

const computedState = (state): ComputedState => ({
  ...userInfoComputedState(state)
});

const createBoundStore = () => {
  return createStore<BoundStore & ComputedState>()(
    devtools(
      subscribeWithSelector(
        persist(
          computed(
            immer((set, get, store) => ({
              ...baseSlice(set),
              ...userInfoSlice(set, get),
              _hasHydrated: false,
              setHasHydrated(state) {
                set({ _hasHydrated: state });
              }
            })),
            computedState
          ),
          {
            name: 'boundStore',
            onRehydrateStorage: () => (state) => {
              state?.setHasHydrated(true);
            }
          }
        )
      )
    )
  );
};

The following screenshot shows the properties of my computedState: image

I merged the type of computedState image

but!!! image

I am configuring this according to the documentation, and the computed properties are fetched as expected, just the typescript exception, what should I do to fix this typescript error?

chrisvander commented 3 weeks ago

Because your create call is createStore<BoundStore & ComputedState>, the innermost middleware (immer in this case) is expecting that the initial store you return include the computed state.

Try using createStore<BoundStore>.

LiuCaptain commented 3 weeks ago

I'm getting typescript errors when I do it your way. image But the code can get the value of the property normally

LiuCaptain commented 3 weeks ago

PS. You can see the ComputedState in my first screenshot

chrisvander commented 3 weeks ago

Could you provide a reproduction in a TypeScript playground? This is the closest I got, but as you can see, the store is properly returning the computed state.

Additionally, you could try explicitly typing the order of mutators on the Immer middleware the way I do in that playground. It appears that, when contained within the computed middleware, the mutators get lost.

chrisvander commented 3 weeks ago

Okay, figured out the issue with the Zustand maintainer. Immer itself may still allow you to "set" the computed state, which should not be possible, but I've opened #2696 to address that issue on Zustand's side. As far as the type inference goes, I'm releasing version 2.0.0 of zustand-computed which should address the type inference issues by using a new pattern, createComputed, which will nicely bundle up the computeState function and any appropriate options and return a middleware for you to use.

Please let me know if there are still any issues.

LiuCaptain commented 2 weeks ago

OK, thanks, I will upgrade to zustand-computed

LiuCaptain commented 2 weeks ago

I upgraded zustand-computed to version 2.0 and still get typescript type errors. image image

Since I'm using zustand-computed in combination with immer middleware and typescript, can you write an example that combines immer and typescripty with zustand-computed, I'd like to see the example you've written, or can you update your github docs, thank you!

chrisvander commented 2 weeks ago

Can you make a minimal reproduction of the issue in TypeScript playground, using Zustand 5.0.0-rc1?