zustand typescript persist how to type store
Asked Answered
P

5

22

I've this simple store

interface CartState {
  cart: { [id: string]: CartDto };
  addItem: ({ id, image, name, price }: Omit<CartDto, "quantity">) => void;
  removeItem: (id: string) => void;
  reset: () => void;
}
export const useCart = create<CartState>((set, get) => ({
  cart: {},
  addItem: ({ id, image, name, price }) => {
    set(() => {
      const cart = get().cart;
      if (!cart[id]) {
        cart[id] = {
          id,
          image,
          name,
          price,
          quantity: 0
        };
      }

      cart[id].quantity += 1;

      return { cart };
    });
  },
  removeItem: (id) => {
    set(() => {
      const cart = get().cart;

      if (!cart[id]) {
        return { cart };
      }

      const newQuantity = cart[id].quantity - 1;
      if (newQuantity <= 0) {
        delete cart[id];
      } else {
        cart[id].quantity = newQuantity;
      }

      return { cart };
    });
  },
  reset: () => {
    set(() => {
      return { cart: {} };
    });
  }
}));

and it works very well. Problem is when I try to add persist

export const useCart = create<CartState>(
  persist(
    (set, get) => ({
      cart: {},
      addItem: ({ id, image, name, price }) => {
        set(() => {
          const cart = get().cart;
          if (!cart[id]) {
            cart[id] = {
              id,
              image,
              name,
              price,
              quantity: 0
            };
          }

          cart[id].quantity += 1;

          return { cart };
        });
      },
      removeItem: (id) => {
        set(() => {
          const cart = get().cart;

          if (!cart[id]) {
            return { cart };
          }

          const newQuantity = cart[id].quantity - 1;
          if (newQuantity <= 0) {
            delete cart[id];
          } else {
            cart[id].quantity = newQuantity;
          }

          return { cart };
        });
      },
      reset: () => {
        set(() => {
          return { cart: {} };
        });
      }
    }),
    {
      name: "cart",
      getStorage: () => localStorage
    }
  )
);

I've got this error:

Argument of type '(set: SetState, get: GetState, api: StoreApiWithPersist<{ cart: {}; addItem: ({ id, image, name, price }: Omit<CartDto, "quantity">) => void; removeItem: (id: string) => void; reset: () => void; }>) => { ...; }' is not assignable to parameter of type 'StoreApi | StateCreator<CartState, SetState, GetState, StoreApi>'.
Type '(set: SetState, get: GetState, api: StoreApiWithPersist<{ cart: {}; addItem: ({ id, image, name, price }: Omit<CartDto, "quantity">) => void; removeItem: (id: string) => void; reset: () => void; }>) => { ...; }' is not assignable to type 'StateCreator<CartState, SetState, GetState, StoreApi>'.

I've tried with

export const useCart = create<StoreApiWithPersist<CartState>>

with no luck.

What's the right way, please?

Pentapody answered 2/11, 2021 at 16:40 Comment(0)
I
9

Edit: PhilJay's answer is the best answer. This answer is outdated even though marked as the best.

I had the same issue. What I did was:

import create, { GetState, SetState } from 'zustand';
import { devtools, persist, StoreApiWithPersist } from 'zustand/middleware';

const useAssetStore = create<AssetStoreType, SetState<AssetStoreType>, GetState<AssetStoreType>, StoreApiWithPersist<AssetStoreType>>(
persist(
    devtools((set) => ({
        logo: '',
    })),
    { name: 'assetStore', version: 1, getStorage: () => localStorage }
));

So, in your case, you need to explicitly add:

create<CartState, SetState<CartState>, GetState<CartState>, StoreApiWithPersist<CartState>>
Iphigenia answered 3/11, 2021 at 11:51 Comment(1)
As the official docs said, there is no specific type you could add when you add a Zustand middleware. docs.pmnd.rs/zustand/guides/typescript#using-middlewaresDirect
U
59

When using zustand with TypeScript, you should use the curried version of create<T>(...) as stated in the first paragraph of the docs.

Replacing

  export const useCart = create<CartState>( 

by

  export const useCart = create<CartState>()( 

should solve the problem.

Umpteen answered 10/11, 2022 at 12:38 Comment(4)
This answer should be marked as correct imho.Tricrotic
I dont understand but worked.Nesline
@Nesline currying is a workaround for github.com/microsoft/TypeScript/issues/10571Levorotation
This is definitely the right answer.Trudeau
I
9

Edit: PhilJay's answer is the best answer. This answer is outdated even though marked as the best.

I had the same issue. What I did was:

import create, { GetState, SetState } from 'zustand';
import { devtools, persist, StoreApiWithPersist } from 'zustand/middleware';

const useAssetStore = create<AssetStoreType, SetState<AssetStoreType>, GetState<AssetStoreType>, StoreApiWithPersist<AssetStoreType>>(
persist(
    devtools((set) => ({
        logo: '',
    })),
    { name: 'assetStore', version: 1, getStorage: () => localStorage }
));

So, in your case, you need to explicitly add:

create<CartState, SetState<CartState>, GetState<CartState>, StoreApiWithPersist<CartState>>
Iphigenia answered 3/11, 2021 at 11:51 Comment(1)
As the official docs said, there is no specific type you could add when you add a Zustand middleware. docs.pmnd.rs/zustand/guides/typescript#using-middlewaresDirect
C
5

Type persist instead of typing create. So in your case:

export const useCart = create(
  persist<CartState>(
    (set, get) => ({
      cart: {},
      addItem: ({ id, image, name, price }) => {
        set(() => {
          const cart = get().cart;
          if (!cart[id]) {
            cart[id] = {
              id,
              image,
              name,
              price,
              quantity: 0
            };
          }

          cart[id].quantity += 1;

          return { cart };
        });
      },
      removeItem: (id) => {
        set(() => {
          const cart = get().cart;

          if (!cart[id]) {
            return { cart };
          }

          const newQuantity = cart[id].quantity - 1;
          if (newQuantity <= 0) {
            delete cart[id];
          } else {
            cart[id].quantity = newQuantity;
          }

          return { cart };
        });
      },
      reset: () => {
        set(() => {
          return { cart: {} };
        });
      }
    }),
    {
      name: "cart",
      getStorage: () => localStorage // Note that this is not necessary, since localstorage is used by default
    }
  )
);
Conscription answered 26/10, 2023 at 16:17 Comment(0)
T
4

Here's an example from the documentation that demonstrates the implementation in TypeScript:

interface BearState {
  bears: number
  increase: (by: number) => void
}

const useBearStore = create<BearState>()(
  devtools(
    persist(
      (set) => ({
        bears: 0,
        increase: (by) => set((state) => ({ bears: state.bears + by })),
      }),
      {
        name: 'bear-storage',
      }
    )
  )
)

Here you got link: https://github.com/pmndrs/zustand#typescript-usage

Twickenham answered 14/6, 2023 at 9:20 Comment(0)
H
0

I am locked with old TypeScript v4 version on the project. When my store started to grow I started to receive strange error messages:

Argument of type ... is not assignable to type

E.g.

Argument of type false is not assignable to type boolean

Above started to annoying me. I found out that curried approach generates not ideal types. As a solution I made following:

type TPersistState = {
  open: boolean;
};

type TState = TPersistState & {
  toggleOpen: (open?: boolean) => void;
}

const persistState: TPersistState = {
  open: false,
};

const useAppStore = create(
  persist<TState, [], [], TPersistState>(
    (set, get) => ({
      ...persistState,
      toggleOpen: (open?: boolean) => {
        if (typeof open === 'boolean') {
          return set({ open });
        }
        return set({ open: !get().open });
      },
    }),
    {
      name: 'some-name',
      storage: createJSONStorage(() => localStorage),
      partialize: (state) =>
        ({
          open: state.open,
        }) as TPersistState,
    },
  ),
);

Above works without strange TS errors.

Hebephrenia answered 22/4 at 9:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.