vuejs / core

🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
https://vuejs.org/
MIT License
47.54k stars 8.31k forks source link

How to import interface for defineProps #4294

Closed Otto-J closed 1 year ago

Otto-J commented 3 years ago

update: please see:

7394

https://github.com/vuejs/core/issues/4294#issuecomment-1316097560


Version

3.2.1

Reproduction link

https://github.com/Otto-J/vue3-setup-interface-errors/blob/master/src/components/HelloWorld.vue

Steps to reproduce

clone: git clone start: yarn && yarn dev open: master/src/components/HelloWorld.vue modify: import interface from './types'

What is expected?

no error

What is actually happening?

[@vue/compiler-sfc] type argument passed to defineProps() must be a literal type, or a reference to an interface or literal type.


in chinese: 我想把 props的interface抽出去,但是会报错,如果interface组件里定义的就正常渲染 in english: I want to extract the interface of props, but an error will be reported. If the interface component is defined, it will render normally

brolnickij commented 2 years ago

any news..?

xiziliang commented 2 years ago

Is this issue still resolved?

jnarowski commented 2 years ago

+1 for needing this also. I try to wrap all components, especially third-party libraries so I need to be able to export their interfaces into the wrapped components.

WormGirl commented 2 years ago

Any news?

TuringJest commented 2 years ago

@jnarowski here is a little workaround which might work for you if you want to get Intellisense for props from an imported interface on your wrapper. Tested in VsCode with Volar.

Vue will not create props for properties on the imported interface but we still can pass them through with $attrs and then provide Intellisense through our own interface which extends the imported one.

<template>
  <LibraryComponent v-bind="$attrs" /> 
</template>

<script setup lang="ts">
...
// has to be inside the wrapper component
// vue will not know about the properties inside LibraryProps that's why we have to pass them by $attrs
interface MyProps extends LibraryProps {
    // a prop that we add on top of LibraryProps
    myProp?: string
}   

const props = defineProps<MyProps>()
...

If we want to add a default value for props from the imported interface we can add only those to our interface. Props for those will be created as usually and we can supply default values.

One downside at the moment is, that Volar will complain about the props on our interface not being defined in the template section of the wrapper but it's just an IDE issue.

<template>
  <LibraryComponent 
    v-bind="$attrs" 
    :aLibraryProp="aLibraryProp" <-- our default value
  /> 
...

<script setup lang="ts">
... 
// has to be inside the wrapper component
// vue will not know about the properties on LibraryProps that's why we have to pass them with $attrs
interface MyProps extends LibraryProps {
    // a prop that we want to give a default value
    aLibraryProp: string

    // a prop that we add on top of LibraryProps
    myProp?: string
}   

const props = withDefaults(defineProps<MyProps>(), {
   aLibraryProp: 'defaultValue'
}
...

It's a messy workaround but for wrapping library components which come with many props it's still useful.

makshark commented 2 years ago

hi, any news about this? people with vite has a temporary solution with the plugin, but what about vue-cli?

victortrusov commented 2 years ago

@TuringJest thank you for the explanation!

I basically did the same thing in my project with Quasar UI but had to learn it the hard way. I also use inheritAttrs: false to make sure that $attrs will be passed to the right child component and bind props to the child component as well because binding only $attrs is not enough sometimes.

For e.g.

<script setup lang="ts">
  import { QBtnProps } from 'quasar';

  // ✨ Types
  export interface LBtnProps extends QBtnProps {
    someNewProp?: string
  }

  // 🚧 Props
  const props = withDefaults(
    defineProps<LBtnProps>(), { someNewProp: 'someDefaultValue' }
  );

  ...
</script>

<script lang="ts">
  export default {
    inheritAttrs: false
  };
</script>

<template>
  ...
  <QBtn
    v-bind="{ ...$attrs, ...props }"
    ...
  />
  ...
</template>
zhouhuiquan commented 2 years ago

Any news?

Benbinbin commented 2 years ago

It's painful to reuse the props interface :weary: any satisfied solution?

jiangxd2016 commented 2 years ago

Any news?

yyx990803 commented 2 years ago

Note: Please stop posting messages like "any news?" If there is an update, you will see it here. No updates means no updates. Spamming the same message over and over again will not help it get resolved earlier.

AndreiSoroka commented 2 years ago

Question: Is it possible to force webpack to import and then paste the type before compiling?

For example:

code

<script lang="ts" setup>
import { SharedProps } from "./sharedProps.ts";

interface Props extends SharedProps {}
const props = defineProps<Props>(); // The type props.foo can be inferred to be a string
</script>

compile before SFC (it is like webpack.DefinePlugin):

<script lang="ts" setup>
// import { SharedProps } from "./sharedProps.ts";
// auto copy and paste:
interface SharedProps {
  foo: string;
  bar?: number;
}

interface Props extends SharedProps {}
const props = defineProps<Props>(); // The type props.foo can be inferred to be a string
</script>
zhouhuiquan commented 2 years ago

Maybe that will be better. vscode plugin or vite plugin ?

<script lang="ts" setup>
import { SharedProps } from "./sharedProps.ts";

interface Props extends SharedProps {
  // auto copy and paste:
  foo: string;
  bar?: number;
}
const props = defineProps<Props>(); // The type props.foo can be inferred to be a string
</script>
AndreiSoroka commented 2 years ago

My draft solution

Component:

image

Types:

image

All working:

image

Solution:

Add custom loader

        {
          test: /\.vue$/,
          use: {
            loader: path.resolve(__dirname, './WebpackLoaderVueSFCTypes.js'),
          },
        },
image

And write loader (WebpackLoaderVueSFCTypes.js):

const fs = require('fs');
const path = require('path');

const regExpDefineProps = /defineProps<(\w+)>\(\)/;

function getRegExpImportDefault(typeName) {
  return new RegExp(`import type ${typeName} from ['"](.*?)['"]`);
}

function getTypeName(source) {
  return source.match(regExpDefineProps)?.[1];
}

function getTypePath(typeName, source, vueFilePath) {
  if (!typeName) {
    return null;
  }
  // todo need to support `import { Type } from` and multilines
  const filePath = source.match(getRegExpImportDefault(typeName))?.[1];

  // todo need to support aliases like `@/`
  const fullPath = path.resolve(
    path.dirname(vueFilePath),
    filePath.endsWith('.ts') ? filePath : `${filePath}.ts`,
  );

  if (!fs.existsSync(fullPath)) {
    console.warn(new Error(`Not found file ${fullPath}`));
    return null;
  }

  return fullPath;
}

function getTypesFromFile(typeName, typePath) {
  // todo better will analysis ts file, without regexp or replace
  return fs.readFileSync(typePath)
    .toString()
    .replace(/import.*?;.*/g, '')
    .replace(/export.*/g, '');
}

function WebpackLoaderVueSFCTypes(source) {
  const typeName = getTypeName(source);
  const typePath = getTypePath(typeName, source, this.resource);
  if (!typeName || !typePath) {
    return source;
  }

  return source.replace(
    getRegExpImportDefault(typeName),
    getTypesFromFile(typeName, typePath),
  );
}

module.exports = WebpackLoaderVueSFCTypes;

Notice: I stopped developing the loader because the types in the component are comfortable. I need the ability to export from component

AndreiSoroka commented 2 years ago

Ha! I found a better solution! And Webstorm is understand it!

function WebpackLoaderVueSFCTypes(source) {
  if (!source.includes('export type')) {
    return source;
  }

  return source.replaceAll(
    'export type',
    'type',
  );
}

module.exports = WebpackLoaderVueSFCTypes;

Component:

image

Storybook (can import styles from vue component):

image

Import component to other component is supported types

image
aislanmaia commented 2 years ago

@AndreiSoroka what if step type is a complex object instead a Number ?

AndreiSoroka commented 2 years ago

@aislanmaia

hi

for SFC comiler it is not important (link)

image image

P.s. I provided two solutions

  1. Import types into the component
  2. Export types from component

I only use the second option. The first option needs to be improved. The first option is more of a working concept (I don't want to support it).

P.s.s. Will there be any feedback from the core team? Thanks

lzr900515 commented 2 years ago

when will it be supported ??😂

EgorPopovPP commented 2 years ago

@lzr900515 😉 https://github.com/vuejs/core/issues/4294#issuecomment-1128852150

tewshi commented 2 years ago

based on above comments, I got it working by extending the imported interface in the setup script (typescript)



import { PaginationOptions } from '~~types`

interface PaginationProps extends PaginationOptions { }

defineProps<PaginationProps>() // OR with default values, use below
withDefaults(defineProps<PaginationProps>(), { [YOUR DEFAULT VALUES] })
tewshi commented 2 years ago

I just observed this does not work, it only takes away the error, and the props don't get passed down using withDefaults

jblachly commented 2 years ago

Because I ran into this problem* today, but with defineEmits, I am boosting the search-engine signal for defineEmits as this seems to be the overarching issue tracking a single solution for both.

*to clarify, like others I've tried to extract Type literals for use in defineProps and defineEmits from a set of related components and put it along other composables.

ckvv commented 2 years ago

As a workaround, the value can be used instead of the type definition

❌As follows I encapsulated an input component with a label

<script setup lang="ts">
import { ElInput } from "element-plus";
import type { InputEmits, InputProps } from "element-plus";

defineProps<
  InputProps & {
    label?: string;
  }
>();
defineEmits<InputEmits>();
</script>

<template>
  <div class="w-input">
    {{ label }}
    <el-input v-bind="$attrs">
      <template #[slotName]="slotProps" v-for="(slot, slotName) in $slots">
        <slot :name="slotName" v-bind="slotProps" />
      </template>
    </el-input>
  </div>
</template>

The following type error was thrown while referencing

Type '{}' is not assignable to type 'IntrinsicAttributes & Partial<{}> & Omit<Readonly<ExtractPropTypes<__VLS_TypePropsToRuntimeProps<{ readonly type: string; readonly disabled: boolean; readonly modelValue: EpPropMergeType<(new (...args: any[]) => (string | number | null | undefined) & {}) | (() => string | ... 2 more ... | undefined) | ((new (...args...'.
  Type '{}' is missing the following properties from type 'Omit<Readonly<ExtractPropTypes<__VLS_TypePropsToRuntimeProps<{ readonly type: string; readonly disabled: boolean; readonly modelValue: EpPropMergeType<(new (...args: any[]) => (string | number | null | undefined) & {}) | (() => string | number | null | undefined) | ((new (...args: any[]) => (string | ... 2 more ... ...': type, disabled, modelValue, autosize, and 11 more.

✅Below is no error and has correct type hints

<script setup lang="ts">
import { ElInput, inputEmits, inputProps } from "element-plus";
defineProps({
  ...inputProps,
  label: String,
});
defineEmits(inputEmits);
</script>

<template>
  <div class="w-input">
    {{ label }}
    <el-input v-bind="$attrs">
      <template #[slotName]="slotProps" v-for="(slot, slotName) in $slots">
        <slot :name="slotName" v-bind="slotProps" />
      </template>
    </el-input>
  </div>
</template>

OR

<script lang="ts">
import { defineComponent } from "vue";
import { inputProps, inputEmits } from "element-plus";

export default defineComponent({
  props: {
    ...inputProps,
    label: String,
  },
  emits: inputEmits,
});
</script>

<template>
  <div class="w-input">
    {{ label }}
    <el-input v-bind="$attrs">
      <template #[slotName]="slotProps" v-for="(slot, slotName) in $slots">
        <slot :name="slotName" v-bind="slotProps" />
      </template>
    </el-input>
  </div>
</template>
arthurbergmz commented 2 years ago

If you are using Vite, you can use this plugin https://github.com/wheatjs/vite-plugin-vue-type-imports as a patch until this is officially supported.

This is great, thank you!!

alsotang commented 2 years ago

When wrapping a third party component, I want to expose its props.

import { Modal } from "@arco-design/web-vue";

// does not work
// defineProps<InstanceType<typeof Modal>["$props"]>();

// works
export type ModalProps = InstanceType<typeof Modal>["$props"];
export interface Props extends ModalProps {}
defineProps<Props>();
wangrongding commented 2 years ago

If you are using Vite, you can use this plugin https://github.com/wheatjs/vite-plugin-vue-type-imports as a patch until this is officially supported.

That's all for now🫡

EmmanuelQ commented 2 years ago

As someone coming from a strong React + Typescript team I must saying not being able to have shareable reusable types across components is quite a let down. Especially when it comes to things like being able to directly use swagger schema generated Typescript definitions for which I've done a lot of in the past. Is there anyway to help contribute to resolve this?

Dschungelabenteuer commented 2 years ago

Just for the record, Evan You told it would be addressed in Vue 3.3.

lzxb commented 2 years ago

Do you have a release plan?

vdseregin commented 2 years ago

I found some hack. Currently, it works for me. Just wrap imported type with Omit.

<script setup lang="ts">
  import { Button as AButton, ButtonProps } from "ant-design-vue";
  interface Props extends Omit<ButtonProps, ""> {}

  const props = defineProps<Props>();
</script>

<template>
  <AButton v-bind="props">
    <slot />
  </AButton>
</template>
Shinigami92 commented 2 years ago

I found some hack. Currently, it works for me. Just wrap imported type with Omit.

<script setup lang="ts">
  import { Button as AButton, ButtonProps } from "ant-design-vue";
  interface Props extends Omit<ButtonProps, ""> {}

  const props = defineProps<Props>();
</script>

<template>
  <AButton v-bind="props">
    <slot />
  </AButton>
</template>

I can approve that this works 👀 dafuq But it makes my code 1 line longer in comparison to the prev workaround + I get a eslint warning for an empty interface 🙈 I would still prefer an official solution

vdseregin commented 2 years ago

I found some hack. Currently, it works for me. Just wrap imported type with Omit.

<script setup lang="ts">
  import { Button as AButton, ButtonProps } from "ant-design-vue";
  interface Props extends Omit<ButtonProps, ""> {}

  const props = defineProps<Props>();
</script>

<template>
  <AButton v-bind="props">
    <slot />
  </AButton>
</template>

I can approve that this works 👀 dafuq But it makes my code 1 line longer in comparison to the prev workaround + I get a eslint warning for an empty interface 🙈 I would still prefer an official solution

Yes! I agree with you. It's more like a temporary solution...

alex-dow commented 2 years ago

I found some hack. Currently, it works for me. Just wrap imported type with Omit.

<script setup lang="ts">
  import { Button as AButton, ButtonProps } from "ant-design-vue";
  interface Props extends Omit<ButtonProps, ""> {}

  const props = defineProps<Props>();
</script>

<template>
  <AButton v-bind="props">
    <slot />
  </AButton>
</template>

If I use this solution and withDefaults, I get a bizarre error:

Unexpected "}"
13 |    setup(__props: any, { emit: emits }) {
14 |  
15 |  const props = __props as  }
   |                            ^
16 |  
17 |  ;
myesn commented 2 years ago

Many thanks to Evan You and the rest of the community for developing and maintaining Vue, but I'm sorry I have to comment because this issue has been around for a year and I don't mean to rush you to fix it, I just want to know if there's a temporary solution, thanks again for your efforts.

soerenmartius commented 2 years ago

Many thanks to Evan You and the rest of the community for developing and maintaining Vue, but I'm sorry I have to comment because this issue has been around for a year and I don't mean to rush you to fix it, I just want to know if there's a temporary solution, thanks again for your efforts.

Have you seen this https://github.com/vuejs/core/issues/4294#issuecomment-984033739 ?

myesn commented 2 years ago

Many thanks to Evan You and the rest of the community for developing and maintaining Vue, but I'm sorry I have to comment because this issue has been around for a year and I don't mean to rush you to fix it, I just want to know if there's a temporary solution, thanks again for your efforts.

Have you seen this #4294 (comment) ?

@soerenmartius Thank you very much for your reply! I really didn't notice this comment because there were so many comments in this issue that I didn't read them carefully, this looks like a viable solution, thank you again!😊

benounnas commented 2 years ago

i tried most of these workarounds, how it's not supported yet? the vite plugin works but it's a little bit frustrating when facing this issue

hrishikesh-k commented 2 years ago

I found that we can used it this way:

<script
  setup
  lang = "ts">
  import type {Foo} from 'bar'
  const props = withDefaults(defineProps<{
    foo : Foo
  }>(), {
    foo: () => {
      return {
        fooProperty1: default1
      }
    }
  })
</script>

Basically, by declaring a property on the prop which can have the required Typing.

kbbdy commented 2 years ago

Maybe it's silly solution, but adding not fully working plugins to vite is not an option for me. So in meantime until Vue 3.3 will be ready I just define types used in props in component and export them. Then this type is imported in types.ts and reexported. This way I still import all types elsewhere from 'types.ts', so when vue 3.3 will be ready, I just move definitions to that file and the rest of the code stays untouched.

In FooField.vue:

<script setup lang="ts">
export type FooFieldProps = {
  foo: string
  bar?: string
}
defineProps<FooFieldProps>()
</script>

then in types.ts:

export type { FooFieldProps } from './FooField.vue'

wangrongding commented 2 years ago

👍Good wit

underdoeg commented 2 years ago

None of the proposed workarounds really worked for all uses cases but it really makes things easier to pass around the prop types to call some shared functions. I don't have a ton of props so I just ended up declaring them twice, once as an interface and once as a PropType. To keep the maintenance of this redundant code easier I came up with these helpers that keep the two in sync:

import {PropType} from "vue";
// copied and slightly modified from vue core
declare type Data = Record<string, unknown>;
declare type DefaultFactory<T> = (props: Data) => T | null | undefined;

type OptionalRequired<T> = undefined extends T ? { required?: false } : { required: true };

declare interface PropOptionsWorkaroundRequired<T = any, D = T> {
    type?: PropType<T> | true | null;
    default?: D | DefaultFactory<D> | null | undefined | object;

    validator?(value: unknown): boolean;
}

// convert interface properties to property options
type PropsWorkaround<T> = {
    [Key in keyof T]-?: PropOptionsWorkaroundRequired<T[Key]> & OptionalRequired<T[Key]>
}

to be used like this

interface InterfaceType {
    foo: string
}

export interface PropsInterface {
    text: string,
    textOptional?: string,
    someNumber?: number,
    interface: InterfaceType
}

export const props_from_interface: PropsWorkaround<PropsInterface> = {
    text: {type: String, required: true},
    textOptional: {type: String},
    someNumber: {type: Number},
    interface: {type: Object as PropType<InterfaceType>, required: true}
}

// and somewhere else
import {props_from_interface} from "file"
const props = defineProps(props_from_interface);

Of course it is less than ideal to have redundant code but at least this way props_from_interface will not compile if any properties from PropsInterface are not implemented. And it is easy enough to add a new property in two places instead of just one until a fix lands in vue.

wynn-w commented 2 years ago

when this type will be used in other files.

// types.ts
interface Test {
  a: string;
}

// component.vue
<script lang="ts" setup>
  import type { Test } from 'types';
  type Props<T extends unkown> = {
    [P in keyof T]: T[P];
  }
 defineProps<Props<Test>>()
</script>

// other.ts
import type { Test } from 'types';
// ...
f3oall commented 2 years ago

Any progress on a real fix for this issue?

underdoeg commented 2 years ago

Any progress on a real fix for this issue?

AFAIK this is on the roadmap for the vue 3.3 release

jsahkjfhasjhfas commented 2 years ago

2022.09.21 这个问题依然存在🙈

dparish commented 2 years ago

Will the fix for this get back ported to 2.7?

We keep our composables and components separate (makes testing VERY easy). It would make for much cleaner code if the prop type and emit types were declared in the composable file.

Example of what we do:

https://stackoverflow.com/questions/72890865/how-to-test-computed-value-inside-setup-function-in-vue-js-3-with-vue-test-utils/73393548#73393548

Mon-ius commented 2 years ago

So, the typescript in Vue3.x is a fake concept;

I first found the props concept can work in Vue3.x along with ts that way, which make me feel like home at first glance;

For example, in foo.vue;

import { defineProps } from 'vue'

interface Props {
    foo: string;
    bar?: number;
}
const props = defineProps<Props>();

However; while I am trying make a little step over, like called my own interface existed in another Props.ts file.

export interface Props {
    foo: string;
    bar?: number;
}

Then, import it to make everything more clear;

import { defineProps } from 'vue'
import Props from '@/utils/Props'

const props = defineProps<Props>();

Vue3.x shows everything just fucks up here:

type argument passed to defineProps() must be a literal type, or a reference to an interface or literal type.
paddotk commented 2 years ago

@Mon-ius The entire discussion is about just that.

juslintek commented 2 years ago

Interfaces work until you put them in a different file ant try to import... Tried with type import regular import with export default with export nothing helps always:

[vite] Internal server error: [@vue/compiler-sfc] type argument passed to defineProps() must be a literal type, or a reference to an interface or literal type. src/components/TheContent.vue 2 | import type WizardSteps from "@/interfaces/WizardSteps"; 3 |
4 | defineProps(); | ^^^^^^^^^^^ 5 | 6 |
Plugin: vite:vue src/components/TheContent.vue at error (node_modules/.pnpm/@vue+compiler-sfc@3.2.40/node_modules/@vue/compiler-sfc/dist/compiler-sfc.cjs.js:3589:15) at processDefineProps (node_modules/.pnpm/@vue+compiler-sfc@3.2.40/node_modules/@vue/compiler-sfc/dist/compiler-sfc.cjs.js:3631:17) at Object.compileScript (node_modules/.pnpm/@vue+compiler-sfc@3.2.40/node_modules/@vue/compiler-sfc/dist/compiler-sfc.cjs.js:4105:17) at resolveScript (file://node_modules/.pnpm/@vitejs+plugin-vue@3.1.0_vite@3.1.4+vue@3.2.40/node_modules/@vitejs/plugin-vue/dist/index.mjs:266:31) at genScriptCode (file:///node_modules/.pnpm/@vitejs+plugin-vue@3.1.0_vite@3.1.4+vue@3.2.40/node_modules/@vitejs/plugin-vue/dist/index.mjs:2346:18) at transformMain (file:///node_modules/.pnpm/@vitejs+plugin-vue@3.1.0_vite@3.1.4+vue@3.2.40/node_modules/@vitejs/plugin-vue/dist/index.mjs:2167:54) at TransformContext.transform (file:///node_modules/.pnpm/@vitejs+plugin-vue@3.1.0_vite@3.1.4+vue@3.2.40/node_modules/@vitejs/plugin-vue/dist/index.mjs:2642:16) at Object.transform (file:///node_modules/.pnpm/vite@3.1.4/node_modules/vite/dist/node/chunks/dep-6b3a5aff.js:41103:44) at async loadAndTransform (file:///node_modules/.pnpm/vite@3.1.4/node_modules/vite/dist/node/chunks/dep-6b3a5aff.js:37365:29)

juslintek commented 2 years ago

Maybe it's silly solution, but adding not fully working plugins to vite is not an option for me. So in meantime until Vue 3.3 will be ready I just define types used in props in component and export them. Then this type is imported in types.ts and reexported. This way I still import all types elsewhere from 'types.ts', so when vue 3.3 will be ready, I just move definitions to that file and the rest of the code stays untouched.

In FooField.vue:

<script setup lang="ts">
export type FooFieldProps = {
  foo: string
  bar?: string
}
defineProps<FooFieldProps>()
</script>

then in types.ts:

export type { FooFieldProps } from './FooField.vue'

I get for export modifier, when it is using setup attribute in script:

TS1184: Modifiers cannot appear here.

I guess I'll stick with duplication of interfaces for time being, and later on will move them into their files. 🙂

Also there is problem with alias in vite.config.ts and tsconfig.json, it does not work:

import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path';
import { VitePluginFonts } from 'vite-plugin-fonts'

// https://vitejs.dev/config/
export default defineConfig({
    resolve: {
        alias: {
            '@/': path.resolve(__dirname, './src')
        },
    },
    plugins: [
        VitePluginFonts({
          google: {
            preconnect: true,
            display: 'swap',
            injectTo: 'head-prepend',
            families: [
              {
                name: 'Montserrat',
                styles: 'ital,wght@0,400;0,800;1,200',
                defer: true,
              },
            ],
          },
        }),
        vue()
    ]
})

and tsconfig.json:

{
  "compilerOptions": {
    "target": "ESNext",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "lib": ["ESNext", "DOM"],
    "skipLibCheck": true,
    "baseUrl": "src",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

Output:

Failed to resolve import "@/components/TheHeader.vue" from "src/App.vue". Does the file exist?
4:54:40 PM [vite] Internal server error: Failed to resolve import "@/components/TheHeader.vue" from "src/App.vue". Does the file exist?
  Plugin: vite:import-analysis
  File: src/App.vue
  1  |  import { defineComponent as _defineComponent } from "vue";
  2  |  import TheHeader from "@/components/TheHeader.vue";
     |                         ^
  3  |  import TheContent from "@/components/TheContent.vue";
  4  |  const _sfc_main = /* @__PURE__ */ _defineComponent({
      at formatError (file:///node_modules/.pnpm/vite@3.1.4/node_modules/vite/dist/node/chunks/dep-6b3a5aff.js:40854:46)
      at TransformContext.error (file:///node_modules/.pnpm/vite@3.1.4/node_modules/vite/dist/node/chunks/dep-6b3a5aff.js:40850:19)
      at normalizeUrl (file:///node_modules/.pnpm/vite@3.1.4/node_modules/vite/dist/node/chunks/dep-6b3a5aff.js:37587:33)
      at processTicksAndRejections (node:internal/process/task_queues:96:5)
      at async TransformContext.transform (file:///node_modules/.pnpm/vite@3.1.4/node_modules/vite/dist/node/chunks/dep-6b3a5aff.js:37720:47)
      at async Object.transform (file:///node_modules/.pnpm/vite@3.1.4/node_modules/vite/dist/node/chunks/dep-6b3a5aff.js:41103:30)
      at async loadAndTransform (file:///node_modules/.pnpm/vite@3.1.4/node_modules/vite/dist/node/chunks/dep-6b3a5aff.js:37365:29)