typescriptvue.jsvuejs3

Module '"vue"' has no exported member 'defineAsyncComponent'


I am trying to convert a large Vue3 app, using the Options API, to TypeScript and am running into a few difficulties. I wonder if someone might give me some guidance. I understand that it is recommended to use the Composition API in conjunction with TypeScript, but sadly that is not an option - the app is already using the Options API and there is no appetite for migrating to the Composition API.

To get started I scaffolded out a new Vu3 + TypeScript app using create-vue and then copied the configs over to the existing app. I also installed any dependencies the existing app was missing which seemed related to TypeScript (e.g @vue/tsconfig and vue-tsc).

Following this, the app appears to be working as expected (serve is working and I can run the app in the browser).

However, as soon as change the script tag in any .vue file to be lang="ts" then any imports from 'vue' are underlined by VSCode. For example, defineAsyncComponent is underlined and VSCode reports that "module 'vue' has no exported member 'defineAsyncComponent'" which is clearly not correct as, if I open the Vue d.ts file in VSCode I can see the declaration for defineAsyncComponent is definitely exported. Plus the app still runs in the browser without issue.

I don't have many extensions installed in VScode, but I do have the official Vue extension, as well as Vetur Extended. I have tried disabling both of these but without the official Vue extension enabled, I don't get any intellisense or even code-colouring in VSCode at all, so I can't even see if the imports are recognised. Disabling only the Vetur Extended extension does not resolve the issue.

It is only Vue imports that have this issue, other 3rd party imports are not underlined, and I can ctrl+click to jump to the associated d.ts file.

What am I missing? How can I make VSCode see things exported from Vue correctly?

For ref, here is my configs:

tsconfig.json

{
  "references": [
    {
      "path": "./tsconfig.node.json"
    },
    {
      "path": "./tsconfig.app.json"
    }
  ],
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

tsconfig.app.json

{
  "extends": "@vue/tsconfig/tsconfig.dom.json",
  "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
  "exclude": ["src/**/__tests__/*"],
  "compilerOptions": {
    "outDir": "./wwwroot/dist",
    "allowJs": true,
    "strict": false,
    "noEmit": false,
    "composite": true,
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo"
  }
}

package.json (relevant parts only)

"dependencies": {
    "vite-plugin-require": "1.2.14",
    "vue": "3.5.6",
    "vue-i18n": "10.0.1",
    "vue-router": "4.4.5",
},
"devDependencies": {
    "@tsconfig/node20": "^20.1.4",
    "@types/node": "^22.8.6",
    "@vitejs/plugin-vue": "5.1.3",
    "@vue/tsconfig": "^0.5.1",
    "rollup-plugin-copy": "3.5.0",
    "sass": "1.79.1",
    "typescript": "^5.6.3",
    "vite": "5.4.6",
    "vue-tsc": "^2.1.10"
}

Edit

I should probably mention, in the reference app I scaffolded out to get the config from initially, I can import defineAsyncComponent without any issues, and VSCode does not underline it. I'm not saying definitely that VSCode is not the issue here, but it seems unlikely given that the ref app doesn't have the same issue when opened with the same version of VScode (with the same extensions). FWIW, I'm using the latest version of VSCode at the time of posting this question (version 1.95.2)

Edit 2

I noticed another difference - in the scaffolded reference app, the tsconfig.app.json does not have the noEmit option set. In the app I am trying to get TypeScript working properly in, if I don't set "noEmit": false then the main tsconfig.json gets an error in the references config which says "tsconfig.app.json may not disable emit".


Solution

  • The config issue that I originally asked the question for - the error in Vue files that "Vue has no exported member 'defineAsyncComponent'" seems to be caused by removing the files option from the root tsconfig, even though the value for that option is just an empty array.

    With "files": [] in the root tsconfig.json file, VSCode is able to use the correct tsconfig.app.json file when I open a .vue file, and imports from 'vue' are not underlined with red.

    If I remove the files option from the root tsconfig, then VSCode reverts to using the root tsconfig rather than tsconfig.app and all the red underlining comes back for any .vue files.

    So this aspect seems solved at least. I still have some other weird issues happening, but I'll open a more focused question for that issue.