vue-i18n icon indicating copy to clipboard operation
vue-i18n copied to clipboard

Typescript Import issue with NodeNext

Open aphex opened this issue 2 years ago • 8 comments
trafficstars

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

aphex avatar Feb 04 '23 22:02 aphex

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".

unshame avatar Feb 28 '23 20:02 unshame

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,

aphex avatar Mar 20 '23 08:03 aphex

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.

Basaingeal avatar Mar 20 '23 14:03 Basaingeal

"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"
  },

RebeccaStevens avatar Mar 27 '23 07:03 RebeccaStevens

"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

Basaingeal avatar Apr 21 '23 15:04 Basaingeal

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:

  1. Change node_modules/vue-i18n/package.json accordingly (see here)
  2. Run npx patch-package vue-i18n --exclude 'nothing'
  3. 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

lupas avatar May 01 '23 09:05 lupas

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.

haoqunjiang avatar May 09 '23 03:05 haoqunjiang

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.

kazupon avatar May 09 '23 06:05 kazupon