Issue with vue-class-component: encountering TS2339 error when trying to call a method within

My vuejs application is being built using vue-cli-service.

After a successful build, I encountered TS2339 errors in my webstorm IDE:

Test.vue:

<template>
    <div>{{method()}}</div>
</template>

<script lang="ts">
    import { Component, Vue } from 'vue-property-decorator';

    @Component
    export default class Test extends Vue {
        public method(): string {
            return 'hello';
        }
    }
</script>

Test.spec.ts:

import 'jest';
import {mount} from '@vue/test-utils';
import Test from '@/views/common/Test.vue';

describe('Test.vue', () => {
    let wrapper: any;

    beforeEach(() => {
        wrapper = mount(Test);
    });

    test('test method call', () => {
        const test = wrapper.find(Test).vm as Test;
        expect(test.method()).toEqual('hello');
    });
});

The error message in Test.spec.ts reads as follows, appearing in both the editor and typescript window:

Error:(14, 21) TS2339: Property 'method' does not exist on type 'Vue'.

Despite the error message, the test runs successfully at runtime when calling test.method().

Answer №1

After reviewing Steven's response, it became clear to me that shims-vue.d.ts is essential for utilizing components as typescript classes. However, the issue lies in them being recognized solely as Vue instances. This fact is evident when examining the contents of this file:

declare module '*.vue' {
  import Vue from 'vue';
  export default Vue;
}

Currently, the most effective solution I have found is to define an interface implemented by my component:

model.ts:

export interface ITest {
    method(): void;
}

Test.vue:

<template>
    <div>{{method()}}</div>
</template>

<script lang="ts">
    import { Component } from 'vue-property-decorator';
    import Vue from 'vue';
    import {ITest} from '@/views/test/model';

    @Component
    export default class Test extends Vue implements ITest {
        public method(): string {
            return 'hello';
        }
    }
</script>

Test.spec.ts:

import 'jest';
import {mount} from '@vue/test-utils';
import {ITest} from '@/views/test/model';
import Test from '@/views/test/Test.vue';

describe('Test.vue', () => {
    let wrapper: any;

    beforeEach(() => {
        wrapper = mount(Test);
    });

    test('test method call', () => {
        const test = wrapper.find(Test).vm as ITest;
        expect(test.method()).toEqual('hello');
    });
});

Answer №2

Upon discovering that Vue files can utilize other Vue files as their declared classes, I decided to experiment by declaring Jest files as Vue components too. To my surprise, it worked seamlessly without the need for additional test-only interfaces.

This process involves two simple steps. First, append the .vue extension to Jest's test configuration in your package.json:

{
  "jest": {
    "testMatch": [
      "**/__tests__/**/*.test.ts",
      "**/__tests__/**/*.test.vue"
    ],
  }
}

Next, rename your test files to have the .test.vue extension and enclose them within a <script> block:

<script lang="ts">
import 'jest';

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

// Your tests go here...
</script>

With this setup, you can now reference wrapper.vm as the actual declared component class type, ensuring compatibility with Vetur/Typescript in both the IDE and the compiler output.

Answer №3

Don't forget to include these lines above your function call.

// tslint:disable-next-line 
// @ts-ignore 

If you want to combine the current Test interface, you can do it like so:

const test = wrapper.find(Test).vm as Test & {method()};

I'm not saying you should follow this practice, but your code will work...

The appropriate solution is to augment Vue's definition so that typescript will recognize your method. However, Vue should handle this automatically. Have you included the shims-vue.d.ts file? That's where the typescript magic happens.

https://v2.vuejs.org/v2/guide/typescript.html

In my experience, I've faced challenges with the Vue class syntax and resorted to using traditional syntax to avoid typescript errors:

<script lang="ts">
    import Vue from 'vue';

    export default Vue.extend({
        methods: {
          method(): string {
            return 'hello';
        }
    })
</script>

The shims file helps Vue integrate with your components.

shims-vue.d.ts

declare module '*.vue' {
  import Vue from 'vue';
  export default Vue;

Dealing with Multiple Vue Instances

There are times when Vue is imported from various sources causing issues with typescript.

Consider adding this to your tsconfig file.

{
    "paths": {
      "@/*": [
        "src/*"
      ],
      "vue/*": [
        "node_modules/vue/*"
      ]
}

In some cases, I've even had to include a webpack alias for this (however, this may cause build problems and is not a direct solution to your issue):

        'vue$': path.resolve(__dirname, 'node_modules', 'vue/dist/vue.esm.js'),

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Creating table structure dynamically using Node.js

I'm attempting to automatically create a table using nodejs "@google-cloud/bigquery": "^3.0.0" with the following approach: const bigqueryClient = new BigQuery(); schema = `driverId:string, passengerIds:(repeated string), pickedUp:(repeated s ...

What is the best way to create and implement custom declaration files that are not available on @types or DefinitelyTyped?

I have encountered a situation where I am using an npm package named foo that is not available on DefinitelyTyped or may be outdated. Despite this, I still want to consume it under stricter settings like noImplicitAny, so I need to create custom definition ...

Why would triggering an input event have any effect if it doesn't appear to be utilized anywhere?

We have developed a custom Vuetify 3 component known as FilterPanel. Here is a simplified version: <template> <div> <v-container> <v-row> <v-col cols="3"> <v-select v-model="fiel ...

The Vue input components are failing to render properly following the integration of different projects

Currently, I am in the process of integrating an established Vue/Vuetify project into Ionic. While most aspects are functioning smoothly, I have encountered a challenge with rendering form inputs correctly - they all seem to be arranged vertically: <tem ...

How can I make sure that another function will only be executed after the completion of a function in

I'm currently working on an app with Angular CLI, and I am trying to retrieve a list after an insertion. Despite trying various methods such as observer, promise, async, setTimeout, etc., I haven't been able to find the right solution yet. I feel ...

Creating a factory class in Typescript that incorporates advanced logic

I have come across an issue with my TypeScript class that inherits another one. I am trying to create a factory class that can generate objects of either type based on simple logic, but it seems to be malfunctioning. Here is the basic Customer class: cla ...

Utilizing a JSDoc comment from an external interface attribute

Currently, I am in the process of developing a React application. It is common to want the props of a child component to be directly connected to the state of a parent component. To achieve this, I have detailed the following instructions in the interface ...

The Vue-router is constantly adding a # symbol to the current routes, and it's important to note that this is not the typical problem of hash and

After setting up my router file using Vue 3, it looks like this: import { createRouter, createWebHistory } from "vue-router"; import Home from "../views/Home.vue"; const routes = [ { path: "/", name: &quo ...

Merging two arrays in Typescript and incrementing the quantity if they share the same identifier

I am currently working on my Angular 8 project and I am facing a challenge with merging two arrays into one while also increasing the quantity if they share the same value in the object. Despite several attempts, I have not been able to achieve the desired ...

Learn how to showcase specific row information in a vuetify data table on Vue.js by implementing icon clicks

I am currently working on a Vuetify data table that showcases order information, with an option for users to cancel their orders by clicking on the cancel icon. Upon clicking the cancel icon, a confirmation overlay pops up displaying the specific order id. ...

Oops! It seems like there is an issue with reading the property 'filter' of an undefined object. Any ideas on how to resolve this error

Having an issue with a custom filter that is causing an error "ERROR TypeError: Cannot read property 'filter' of undefined". I need help fixing this as it's preventing anything from rendering on the page. Any suggestions on what changes I sh ...

Utilize Aframe to easily view and upload local gltf files

I've been working on a project to create a user-friendly panel for loading and viewing gltf models in real-time in A-frame. Here is the current workflow I am following: Using the input tag to load files from local storage. Using v-on-change to assi ...

What causes functions operating on mapped objects with computed keys to not correctly infer types?

If you are seeking a way to convert the keys of one object, represented as string literals, into slightly modified keys for another expected object in Typescript using template string literals, then I can help. In my version 4.9.5 implementation, I also ma ...

Send a variable from a next.js middleware to an API request

I've been attempting to pass a middleware variable to my API pages via "req" but have encountered some issues Even after trying to send the user token to pages using "req", it consistently returns null The middleware file in question is: pages/api/u ...

Displaying a disabled div depending on the dropdown selection in Angular Material

My goal is to display filters in a dropdown after they have been selected. Currently, I have static disabled divs and a dropdown where filters can be selected. This is the dropdown: <mat-form-field> <mat-label>{{ 'supplier.showFilters&a ...

Encountering a challenge when upgrading to eslint version 9.0.0

Encountering an issue while trying to upgrade eslint to version 9.0.0. ⋊> ~/A/fusion on turborepo ⨯ bun lint 22:21:58 $ eslint packages/*/src/**/* Oops! Something went wrong! :( ESLint: 9.0. ...

Allowing the use of a string as a parameter in a Typescript constructor

Currently, I am utilizing TypeScript to create a constructor for a model within Angular. One of the attributes in the model is configured as an enum with specific string values. Everything functions well if an enum value is passed to the constructor. The i ...

I have encountered limitations with useFormik where it does not accept null values for initialValues, even while utilizing a validationSchema

I am currently utilizing the useFormik hook to handle my form. The userId field is a select, so by default its value is set to null. However, my validationSchema requires this field to be populated before submission. const formik = useFormik<ApiCredit ...

My Babel-Loader seems to be failing in converting the vendor code to be compatible with IE8+. What might be causing this issue?

I'm currently utilizing the most recent versions of Webpack, Babel, and Babel-Loader in my Vue.js application. My goal is to ensure that my code runs smoothly on Internet Explorer 8, 9, and 10, but unfortunately, I am facing difficulties with this. ...

A useful tip for adding a blank line in a specific index of an array is to utilize the character while iterating through the array with a v-for loop to generate a list

I am trying to find a way to add an empty line before a specific array element while iterating through it using v-for to generate a list. Using \n does not seem to be effective for this task. <!-- Here is the template section --> <ul> ...