Skip to content

Type-checking fails with Vue 2.7.9+ #2026

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
haoqunjiang opened this issue Nov 8, 2022 · 10 comments
Open

Type-checking fails with Vue 2.7.9+ #2026

haoqunjiang opened this issue Nov 8, 2022 · 10 comments

Comments

@haoqunjiang
Copy link
Member

Subject of the issue

After updating vue to 2.7.9 or above, vue-tsc throws an type error on the mount() call.

Seems triggered by https://github.com/vuejs/vue/pull/12727/files

Steps to reproduce

pnpm create vue@2 --typescript --vitest vue2-test-utils-type-checking
cd vue2-test-utils-type-checking
pnpm i
pnpm type-check

Expected behaviour

The type-checking should succeed without errors.

Actual behaviour

src/components/__tests__/HelloWorld.spec.ts:8:27 - error TS2769: No overload matches this call.
  The last overload gave the following error.
    Argument of type 'ComponentPublicInstanceConstructor<Vue3Instance<{}, Readonly<ExtractPropTypes<__VLS_TypePropsToRuntimeProps<{ msg: string; }>>>, Readonly<ExtractPropTypes<__VLS_TypePropsToRuntimeProps<{ msg: string; }>>>, {}, {}, true, ComponentOptionsBase<...>> & ... 4 more ... & Readonly<...>, ... 4 more ..., MethodOptions> & Com...' is not assignable to parameter of type 'ExtendedVue<Vue<Record<string, any>, Record<string, any>, never, never, (event: string, ...args: any[]) => Vue<Record<string, any>, Record<string, any>, never, never, ...>>, ... 6 more ..., ComponentOptionsMixin>'.
      Type 'ComponentPublicInstanceConstructor<Vue3Instance<{}, Readonly<ExtractPropTypes<__VLS_TypePropsToRuntimeProps<{ msg: string; }>>>, Readonly<ExtractPropTypes<__VLS_TypePropsToRuntimeProps<{ msg: string; }>>>, {}, {}, true, ComponentOptionsBase<...>> & ... 4 more ... & Readonly<...>, ... 4 more ..., MethodOptions> & Com...' is missing the following properties from type 'VueConstructor<ExtractComputedReturns<{}> & DefaultProps & Vue<Record<string, any>, Record<string, any>, never, never, (event: string, ...args: any[]) => Vue<...>> & ShallowUnwrapRef<...> & Vue<...>>': extend, nextTick, set, delete, and 10 more.

8     const wrapper = mount(HelloWorld, { propsData: { msg: 'Hello Vitest' } })
                            ~~~~~~~~~~

  node_modules/.pnpm/@[email protected]_2s2ymob7v2oigx3hqbmnjuqthq/node_modules/@vue/test-utils/types/index.d.ts:195:25
    195 export declare function mount<V extends Vue, Props = DefaultProps> (component: ExtendedVue<V, {}, {}, {}, Props>, options?: FunctionalComponentMountOptions<V>): Wrapper<CombinedVueInstance<V, {}, {}, {}, Props> & Vue>
                                ~~~~~
    The last overload is declared here.


Found 1 error in src/components/__tests__/HelloWorld.spec.ts:8

Possible Solution

I don't know…

@OneLoneFox
Copy link

OneLoneFox commented Dec 9, 2022

Can confirm, bug is still reproducible on a fresh project with ts, running the build command which runs run-p type-check build-only or directly running type-check results in the following output:

warning package.json: No license field
$ run-p type-check build-only
warning package.json: No license field
warning package.json: No license field
$ vite build
$ vue-tsc --noEmit -p tsconfig.vitest.json --composite false
vite v3.2.5 building for production...
✓ 44 modules transformed.
src/components/__tests__/HelloWorld.spec.ts:8:27 - error TS2769: No overload matches this call.
  The last overload gave the following error.
    Argument of type 'ComponentPublicInstanceConstructor<Vue3Instance<{}, Readonly<ExtractPropTypes<__VLS_TypePropsToRuntimeProps<{ msg: string; }>>>, Readonly<ExtractPropTypes<__VLS_TypePropsToRuntimeProps<{ msg: string; }>>>, {}, {}, true, ComponentOptionsBase<...>> & ... 4 more ... & Readonly<...>, ... 4 more ..., MethodOptions> & Com...' is not assignable to parameter of type 'ExtendedVue<Vue<Record<string, any>, Record<string, any>, never, never, (event: string, ...args: any[]) => Vue<Record<string, any>, Record<string, any>, never, never, ...>>, ... 6 more ..., ComponentOptionsMixin>'.
      Type 'ComponentPublicInstanceConstructor<Vue3Instance<{}, Readonly<ExtractPropTypes<__VLS_TypePropsToRuntimeProps<{ msg: string; }>>>, Readonly<ExtractPropTypes<__VLS_TypePropsToRuntimeProps<{ msg: string; }>>>, {}, {}, true, ComponentOptionsBase<...>> & ... 4 more ... & Readonly<...>, ... 4 more ..., MethodOptions> & Com...' is missing the following properties from type 'VueConstructor<ExtractComputedReturns<{}> & DefaultProps & Vue<Record<string, any>, Record<string, any>, never, never, (event: string, ...args: any[]) => Vue<...>> & ShallowUnwrapRef<...> & Vue<...>>': extend, nextTick, set, delete, and 10 more.

8     const wrapper = mount(HelloWorld, { propsData: { msg: 'Hello Vitest' } })
                            ~~~~~~~~~~

  node_modules/@vue/test-utils/types/index.d.ts:195:25
    195 export declare function mount<V extends Vue, Props = DefaultProps> (component: ExtendedVue<V, {}, {}, {}, Props>, options?: FunctionalComponentMountOptions<V>): Wrapper<CombinedVueInstance<V, {}, {}, {}, Props> & Vue>
                                ~~~~~
    The last overload is declared here.


Found 1 error in src/components/__tests__/HelloWorld.spec.ts:8

error Command failed with exit code 2.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
ERROR: "type-check" exited with 2.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

@IGx89
Copy link
Contributor

IGx89 commented Feb 14, 2023

Here's a partial fix that worked for me with Vue 2.7.14. More overloads likely need to be added for other scenarios, but it works well for defineComponent components for me at least:

import {ComputedOptions, ComponentOptionsMixin, MethodOptions} from 'vue/types/v3-component-options';
import {ExtractPropTypes, ExtractDefaultPropTypes} from 'vue';
import {EmitsOptions} from 'vue/types/v3-setup-context';
import type {DefineComponent} from 'vue';
import type {Wrapper} from '@vue/test-utils';

// Augment vue-test-utils to add support for Vue 2.7.
declare module '@vue/test-utils' {
	// Component declared with defineComponent
	export function mount<
		PropsOrPropOptions = {},
		RawBindings = {},
		D = {},
		C extends ComputedOptions = ComputedOptions,
		M extends MethodOptions = MethodOptions,
		Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
		Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
		E extends EmitsOptions = Record<string, any>,
		EE extends string = string,
		Props = Readonly<ExtractPropTypes<PropsOrPropOptions>>,
		Defaults extends {} = ExtractDefaultPropTypes<PropsOrPropOptions>
	>(
		component: DefineComponent<PropsOrPropOptions, RawBindings, D, C, M, Mixin, Extends, E, EE, Props, Defaults>,
		options?: any
	): Wrapper<
		InstanceType<
			DefineComponent<PropsOrPropOptions, RawBindings, D, C, M, Mixin, Extends, EmitsOptions, EE, Props, Defaults>
		>
	>;

	// Component declared with defineComponent
	export function shallowMount<
		PropsOrPropOptions = {},
		RawBindings = {},
		D = {},
		C extends ComputedOptions = ComputedOptions,
		M extends MethodOptions = MethodOptions,
		Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
		Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
		E extends EmitsOptions = Record<string, any>,
		EE extends string = string,
		Props = Readonly<ExtractPropTypes<PropsOrPropOptions>>,
		Defaults extends {} = ExtractDefaultPropTypes<PropsOrPropOptions>
	>(
		component: DefineComponent<PropsOrPropOptions, RawBindings, D, C, M, Mixin, Extends, E, EE, Props, Defaults>,
		options?: any
	): Wrapper<
		InstanceType<
			DefineComponent<PropsOrPropOptions, RawBindings, D, C, M, Mixin, Extends, EmitsOptions, EE, Props, Defaults>
		>
	>;
}

(put in a *.d.ts file)

@ascott18
Copy link

ascott18 commented Mar 8, 2023

This works for me, for both defineComponent and <script setup> on vue 2.7.14

import { ComponentPublicInstance } from 'vue';
import { ThisTypedMountOptions } from '@vue/test-utils';

declare module '@vue/test-utils' {
    export function mount<V extends {}>(originalComponent: {
        new (...args: any[]): V;
    }, options?: ThisTypedMountOptions<V>): Wrapper<ComponentPublicInstance<V>>;
    
    export function shallowMount<V extends {}>(originalComponent: {
        new (...args: any[]): V;
    }, options?: ThisTypedMountOptions<V>): Wrapper<ComponentPublicInstance<V>>;
}

@floroz
Copy link

floroz commented Apr 15, 2023

Any stable solution that doesn't lose type information?

I've got the same issue using "@vue/test-utils": "^2.3.2" and "vue": "^3.2.47"

Edit: actually @ascott18 solution worked well for me in the end, thank you :)

@bvogel
Copy link

bvogel commented Jun 15, 2023

We got bitten by this as well this week and wasted 8h until finding this issue and applying the fix from @ascott18, can this be fixed upstream please?

@ZaneL1u
Copy link

ZaneL1u commented Jan 23, 2024

This works for me:

const wrapper = mount(Vue.extend(HelloWorld), { propsData: { msg: 'Hello Vitest' } })

@gnicol-bzh
Copy link

gnicol-bzh commented Mar 7, 2024

Hi,

Issue:

I've been experiencing a similar issue for about a week now. Suddenly, unit tests written with Jest started throwing typing errors. I opened another project that hadn't been modified for about a month and it also now throws errors that didn't exist before, on hundreds of unit tests.

Code example:

import { Wrapper, mount } from '@vue/test-utils';
import MyComponent from '../MyComponent.vue';

describe('MyComponent test description', () => {
    let wrapper: Wrapper<InstanceType<typeof MyComponent>>;
    beforeEach(() => {
        wrapper = mount(MyComponent, { propsData: { name: 'the-name' } });
    });
    it('should test a lot of things', () => {
        [...]
    });
});

Package.json (common part of the 2 projects)

{
    "dependencies": {
        [...]
        "vue": "2.7.16",
        "vuetify": "2.6.9",
        "vuex": "3.6.2",
        "webpack": "5.88.1"
    },
    "devDependencies": {
        "@babel/core": "7.22.8",
        "@babel/preset-env": "7.19.1",
        "@types/jest": "29.5.2",
        "@types/node": "16.11.59",
        "@typescript-eslint/eslint-plugin": "5.62.0",
        "@typescript-eslint/parser": "5.62.0",
        "@vue/cli-plugin-unit-jest": "5.0.8",
        "@vue/cli-service": "5.0.8",
        "@vue/eslint-config-typescript": "11.0.1",
        "@vue/test-utils": "1.3.0",
        "@vue/vue2-jest": "29.2.4",
        "babel-jest": "29.6.1",
        "eslint": "7.32.0",
        "eslint-plugin-vue": "9.20.1",
        "jest": "29.6.1",
        "jest-environment-jsdom": "29.6.1",
        "ts-jest": "29.1.1",
        "ts-loader": "8.2.0",
        "ts-node": "10.9.1",
        "typescript": "4.8.3",
        "vue-eslint-parser": "9.4.2",
        "vue-template-babel-compiler": "2.0.0",
        "vue-template-compiler": "2.7.16"
    }
}

Error:

Type 'Wrapper<Vue<Record<string, any>, Record<string, any>, never, never, (event: string, ...args: any[]) => Vue<Record<string, any>, Record<string, any>, never, never, ...>>, Element>' cannot be assigned to type 'Wrapper<Vue3Instance<{}, Readonly<ExtractPropTypes<{ name: { type: StringConstructor; required: true; }; }>>, Readonly<ExtractPropTypes<{ name: { type: StringConstructor; required: true; }; }>>, {}, {}, true, ComponentOptionsBase<...>> & ... 4 more ... & Readonly<...>, Element>'.
  Type 'Vue<Record<string, any>, Record<string, any>, never, never, (event: string, ...args: any[]) => Vue<Record<string, any>, Record<string, any>, never, never, ...>>' cannot be assigned to type 'Vue3Instance<{}, Readonly<ExtractPropTypes<{ name: { type: StringConstructor; required: true; }; }>>, Readonly<ExtractPropTypes<{ name: { type: StringConstructor; required: true; }; }>>, {}, {}, true, ComponentOptionsBase<...>> & ... 4 more ... & Readonly<...>'.
    Type 'Vue<Record<string, any>, Record<string, any>, never, never, (event: string, ...args: any[]) => Vue<Record<string, any>, Record<string, any>, never, never, ...>>' cannot be assigned to type 'Vue3Instance<{}, Readonly<ExtractPropTypes<{ name: { type: StringConstructor; required: true; }; }>>, Readonly<ExtractPropTypes<{ name: { type: StringConstructor; required: true; }; }>>, {}, {}, true, ComponentOptionsBase<...>>'.
      Types of property '$props' are incompatible.
        Property 'name' is missing in type 'Record<string, any>' but required in type 'Readonly<Partial<{}> & Omit<Readonly<ExtractPropTypes<{ name: { type: StringConstructor; required: true; }; }>>, never>>'.ts(2322)

Additional information:

  • This issue appeared suddenly after a period of no code changes.
  • The issue affects multiple projects, including those that haven't been modified for a while.
  • The error seems to be related to props types and Jest usage, and/or global VSCode settings.

Request:

  • Have you encountered a similar issue?
  • Is there a known solution to this problem?
  • Any information or debugging leads would be greatly appreciated.

Thanks in advance for your help.

@gnicol-bzh
Copy link

For information, I have just solved my problem by reinstalling version 1.8.27 of the VSCode "Vue - official" plugin. Version 2 no longer seems to correctly take Vue 2.7 into account.

@bvogel
Copy link

bvogel commented Mar 13, 2024

Could any maintainer please either fix this or please explain how to deal with this situation it this is expected behavior?

@joaopedrodcf
Copy link

joaopedrodcf commented Mar 16, 2024

Does any of these solutions works for you guys ? What they do for me is that I can use mount of shallowMount but I don't have typescript support in the props, example:

My component:

<template>
  <div class="greetings">{{ msg }}</div>
</template>

<script lang="ts" setup>
import { defineComponent, type SetupContext } from 'vue';

type Props = {
  msg: string
}

defineProps<Props>();
</script>

My test:

import { mount } from '@vue/test-utils';
import { describe, it, expect } from 'vitest';
import HelloWorld from '../HelloWorld.vue';

describe('HelloWorld', () => {
  it('renders properly', () => {
    const wrapper = mount(HelloWorld, {
       propsData: { 
          msg: 'Hello Vitest',  // This property exists
          thisShouldWarnMe: 'test' // This property doesn't exist on the component but I can pass it
       },
    });
    expect(wrapper.text()).toContain('Hello Vitest');
  });
});

Expected behaviour would be that any property that I pass in the tests and doesn't exists in the component should alert me

Made an example using stackblitz template: https://stackblitz.com/edit/github-ttasqp?file=src%2Fcomponents%2F__tests__%2FHelloWorld.spec.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants