zustand: TSC throws an error when compiling with "moduleResolution": "NodeNext"
When I want to import the default create
function from zustand/vanilla
while setting NodeNext
for both module
and moduleResolution
in tsconfig.json
, TSC will throw an error: error TS2349: This expression is not callable.
I mentioned this issue in https://github.com/microsoft/TypeScript/issues/50058#issuecomment-1288155712. However, according to the reply, this is a zustand problem:
@zustand/vanilla
has incorrect types, again, it only declares types for a cjs entrypoint for everything, and that is the result of that mismatch.
Kindly request a fix on this problem, thanks!
In the meantime, I use default-import
as a temporary patch.
this doesn’t work
import create from "zustand/vanilla";
export const store = create(() => ({ foo: "bar" }));
this works
import zustand from "zustand/vanilla";
import { defaultImport } from "default-import";
const create = defaultImport(zustand);
export const store = create(() => ({ foo: "bar" }));
A minimal repo to reproduce this problem: https://stackblitz.com/edit/node-vj368k?file=src/index.ts
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 19 (9 by maintainers)
Sounds great!
Unfortunately no, TSC will use
./vanilla.d.ts
as the resolution result, but if you change the order like this, it will work:I have some promising findings:
If I change the
package.json > exports > ./vanilla > types
field from"./vanilla.d.ts"
to"./esm/vanilla.d.mts"
like this:and add a new file:
dist/esm/vanilla.d.mts
whose content is copied fromdist/esm/vanilla.d.ts
(We can safely copy the content because noimport
orexport
is used invanilla
, so there’s no.js
extension issues, but of course in order for other exported subpaths to work, we’ll have to consider this difference). We can finally get a successful output:As I understand it, the
.d.mts
extension is telling TSC to regard this type file as a type file of a package imported as an esmodule rather than a commonjs module, or otherwise because we don’t havetype: "module"
field in thepackage.json
, TSC seems cannot decide which type of module we are expecting.I think I find the reason why zustand does not work properly as an esm import.
While I was searching for related problems, I bumped into this solved issue: https://github.com/hayes/pothos/issues/597, which was also an esm module resolution problem. The author finally got his package fixed after several pushes and I noticed one of his comments:
and
So I first checked his
package.json
:And according to this configuration, typescript will use
./dts/index.d.ts
when this module is resolved as a cjs module and will use./esm/index.d.ts
when it is resolved as an es module. By comparing these two typedef files, we can find that the.js
extension is needed in all theexport
orimport
statements in an esm typedef file:diff ./esm/index.d.ts ./dts/index.d.ts
And the reason is that typescript doesn’t mutate any
import
orexport
statements when it compiles user’s source code to generate typedefs. Therefore, if a package using typescript is not of typemodule
while planning to support esm build and allowing other esm typescript projects to import it, the package will have to also include typedefs specifically for esm imports.And that’s why this problem occurs in zustand because it only generated cjs typedefs, i.e. no “.js” extensions in the export or import statements:
index.d.ts, this affects
zustand
:middleware/devtools.d.ts, this and other middleware typedefs affect
zustand/vanilla
:I am not familiar with the bundle/build scripts in generating or transforming these files, but I hope the above provided info can help you or other contributors locate the problem and fix it. ❤️