vue-i18n
vue-i18n copied to clipboard
Typescript Import issue with NodeNext
Reporting a bug?
When setting "moduleResolution": "NodeNext", in tsconfig.json and trying to use this package will result in a error in VSCode Could not find a declaration file for module 'vue-i18n'. However everything runs fine, I think this has to do with how exports are being interpreted
Changing the package.json exports to look like this seems to resolve the issue. Just adding types here as well. Is this a change that would be acceptable?
"exports": {
".": {
"import": {
"node": "./index.mjs",
"default": "./dist/vue-i18n.esm-bundler.js"
},
"require": "./index.js",
"types": "./dist/vue-i18n.d.ts"
},
"./dist/*": "./dist/*",
"./index.mjs": "./index.mjs",
"./package.json": "./package.json"
},
Expected behavior
Package should not show dev time TS errors when using NodeNext moduleResolution
Reproduction
Quickest reproduction is to just create a TS vue app using Vite, and add vue-i8n to the project and update the tsconfig.json with "moduleResolution": "NodeNext",
System Info
System:
OS: Linux 5.15 Ubuntu 20.04.4 LTS (Focal Fossa)
CPU: (16) x64 Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz
Memory: 25.84 GB / 31.31 GB
Container: Yes
Shell: 3.2.2 - /usr/bin/fish
Binaries:
Node: 16.17.0 - ~/.nvm/versions/node/v16.17.0/bin/node
Yarn: 1.22.19 - /usr/bin/yarn
npm: 9.1.3 - ~/.nvm/versions/node/v16.17.0/bin/npm
Browsers:
Chrome: 102.0.5005.61
npmPackages:
@intlify/unplugin-vue-i18n: ^0.8.1 => 0.8.1
@vitejs/plugin-vue: ^4.0.0 => 4.0.0
vite: ^4.1.1 => 4.1.1
vue: ^3.2.47 => 3.2.47
vue-i18n: ^9.2.2 => 9.2.2
vue-router: ^4.1.6 => 4.1.6
vue-tsc: ^1.0.24 => 1.0.24
Screenshot
No response
Additional context
No response
Validations
- [X] Read the Contributing Guidelines
- [X] Read the Documentation
- [X] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [X] Check that this is a concrete bug. For Q&A open a GitHub Discussions
Faced the same issue, but patching vue-i18n's package.json doesn't seem to fix it completely, because now calling useI18n gets inferred as Composer<any, any, any, any, any, any>. I have to specify the return type explicitly. I don't really understand why.
Edit: ah, of course, the package.json of @intlify/core-base has to be patched as well, then the types work. I don't really understand why typescript doesn't fallback to the "types" or "typings" field in the root of package.json when it's missing in "exports".
Another update on this, updating to TS 5 and changing moduleResolution to bundler will result in a slightly different error.
Could not find a declaration file for module 'vue-i18n'. 'C:/path/to/node_modules/.pnpm/[email protected][email protected]/node_modules/vue-i18n/dist/vue-i18n.esm-bundler.js' implicitly has an 'any' type.
There are types at 'c:/path/to/node_modules/vue-i18n/dist/vue-i18n.d.ts', but this result could not be resolved when respecting package.json "exports". The 'vue-i18n' library may need to update its package.json or typings.ts(7016)
Patching as I mentioned above still seems to fix this for me,
Evan You is recommending on Twitter that Vue apps set moduleResolution to bundler when using TS 5.0, but that can't be done in apps using this library without setting "resolvePackageJsonExports": false currently because of the error mentioned above.
"exports": { ".": { "import": { "node": "./index.mjs", "default": "./dist/vue-i18n.esm-bundler.js" }, "require": "./index.js", "types": "./dist/vue-i18n.d.ts" }, "./dist/*": "./dist/*", "./index.mjs": "./index.mjs", "./package.json": "./package.json" },
The "types" export needs to come first (for the same reason "default" needs to come last). Additionally, each export should have its own type declaration file, (one for "imports" and one for "require") - this is because TypeScript can't tread a single file as both esm and cjs (and isn't smart enough to automatically switch between them).
A better fix would be:
"exports": {
".": {
"import": {
"types": "./dist/vue-i18n.d.ts",
"node": "./index.mjs",
"default": "./dist/vue-i18n.esm-bundler.js"
},
"require": {
"types": "./dist/vue-i18n.d.cts", // Note: Different file to the one import points to
"default": "./index.js"
}
},
"./dist/*": "./dist/*",
"./index.mjs": "./index.mjs",
"./package.json": "./package.json"
},
"moduleResolution": "bundler" is now the default settings in the latest vuejs/tsconfig, compatible with TS 5.0.
https://github.com/vuejs/tsconfig#migrating-from-typescript--50
Thanks @RebeccaStevens, that fixes it for me. I created an MR with your proposed changes: https://github.com/intlify/vue-i18n-next/pull/1388
Until this is merged you can easily fix this for you locally with patch-package like so:
- Change
node_modules/vue-i18n/package.jsonaccordingly (see here) - Run
npx patch-package vue-i18n --exclude 'nothing' - Add the following to scripts in your own
package.json:
"scripts": {
"postinstall": "npx patch-package"
}
And i18n is working with Typescript again nicely 😊
Related: https://github.com/microsoft/TypeScript/issues/52363
While the type isn't perfect with Node16 + ESM import, the bundler resolution mode already works with the latest 9.3 beta, but not with 9.2.2.
If you will avoid this issue, I would recommend installing the next tag version, (i.e. vue-i18n v9.3). Currently in beta, this version has not changed much in terms of functionality from v9.2. Actually, It's used in nuxt i18n v8 beta.