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/vanillahas 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.tsas 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 > typesfield from"./vanilla.d.ts"to"./esm/vanilla.d.mts"like this:and add a new file:
dist/esm/vanilla.d.mtswhose content is copied fromdist/esm/vanilla.d.ts(We can safely copy the content because noimportorexportis used invanilla, so there’s no.jsextension 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.mtsextension 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.tswhen this module is resolved as a cjs module and will use./esm/index.d.tswhen it is resolved as an es module. By comparing these two typedef files, we can find that the.jsextension is needed in all theexportorimportstatements in an esm typedef file:diff ./esm/index.d.ts ./dts/index.d.ts
And the reason is that typescript doesn’t mutate any
importorexportstatements when it compiles user’s source code to generate typedefs. Therefore, if a package using typescript is not of typemodulewhile 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. ❤️