question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Using subscribeWithSelector and immer middleware

See original GitHub issue

I am using v4.0 (TS 4.5.5) and trying the standard immer middleware and subscribeWithSelector:

export const store = create<
  Store
>()(subscribeWithSelector(immer((set, get) => createRootSlice(set, get))));

but I got this error:

TS4023: Exported variable 'store' has or is using name 'StoreSubscribeWithSelector' from external module "zustand/middleware/subscribeWithSelector" but cannot be named.

Am I doing something wrong, or it is a problem with Zustand v4?

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:14 (9 by maintainers)

github_iconTop GitHub Comments

3reactions
devanshjcommented, Apr 29, 2022

Actually I realized TS4023 occurs only for subscribeWithSelector (and not for other middlewares) because that’s the only middleware that uses interface for intermediate types instead of type alias. So we can fix it for subscribeWithSelector by using a type alias (and we’re already using them instead of interfaces in most places, I usually prefer interface but in this case I think type aliases are fine).

But the .d.ts files still look a bit ugly as it inlines all the types (as it does now also for other middlewares) but I guess that’s okay.

So I’ll open a PR for using type aliases.

2reactions
devanshjcommented, Apr 27, 2022

The problem is with --declaration TS emits .d.ts files which looks something like this…

export declare const useStore: TheType

Now to write the actual type instead of TheType TS needs StoreSubscribeWithSelector in the scope, which it isn’t right now hence it complains.

But useStore’s type actually never directly uses StoreSubscribeWithSelector, it’s type actually is this…

Mutate<
  StoreApi<State>,
  [
    ["zustand/subscribeWithSelector", never],
    ["zustand/immer", never]
  ]
>

It only later resolves to a type that uses StoreSubscribeWithSelector. So typescript should emit the above type, not the one it resolves to (this might be a bug in TS).

So one workaround is to explicitly type useStore with the above type and then TS will emit that…

import create, { Mutate, StoreApi } from "zustand";
import { subscribeWithSelector } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";

type State = {
  count: number;
  inc: () => void;
};

type Store =
  Mutate<
    StoreApi<State>,
    [
      ["zustand/subscribeWithSelector", never],
      ["zustand/immer", never]
    ]
  >

export const useStore: Store = create<State>()(
  subscribeWithSelector(
    immer((set) => ({
      count: 0,
      inc: () => set((state) => ({ count: state.count + 1 }))
    }))
  )
);

This will compile, as you can see here.

Another solution could be that we export StoreSubscribeWithSelector (and the many other types) but that still won’t solve the problem as you’ll also have to import them to bring them in the scope (even if you’re not using them). But even that is problematic because we don’t want to export our intermediate types nor .d.ts should emit them as they are not to consumed directly.

And of course if you can remove --declaration then the problem gets solved entirely.

I personally think it’d make sense that the users explicitly annotate the exports instead of us exporting the types and making a compromise. But I’ll let @dai-shi take the call if we want --declaration (which is false by default) users to annotate their exports or we should export those our types.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Using middlewares - zfy - React Native Modalfy
This snippet is only provided as an example to show you how to integrate a zustand middleware. You shouldn't rely on devtools as...
Read more >
Recipes - Zustand Documentation - Pmndrs.docs
You can, but bear in mind that it will cause the component to update on every ... If you need to subscribe with...
Read more >
zustand - npm
Bear necessities for state management in React. Latest version: 4.1.5, last published: 13 days ago. Start using zustand in your project by ...
Read more >
GitHub - sthose/zustand: Bear necessities for state management in ...
If you need to subscribe with selector, subscribeWithSelector middleware will help. ... import produce from 'immer' const useStore = create(set => ({ lush: ......
Read more >
Daishi Kato on Twitter: "Zustand v3.6.0 is now released! - Twitter
... changed/improved/tricked - New middleware `subscribeWithSelector` ... `subscribeWithSelector` Deprecated features will be removed in v4.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found