3

I'm using vue-cli-service to build my vuejs application.

The build is successful, but in webstorm IDE, I get some TS2339 errors :

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');
    });
});

In Test.spec.ts, I get this error, both in editor and in typescript window:

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

But the test is OK, so test.method() is resolved successfully at runtime.

9
  • Are you missing type definitions? Commented Jun 5, 2019 at 9:36
  • Shouldn't class definition be sufficient? What additional definition would you add here? Commented Jun 5, 2019 at 9:38
  • What are you trying to achieve with 'const value = new Test().value'? It looks like you're trying to either inject a dependency or register the component locally, neither of which are done in the manner you have tried. Commented Jun 5, 2019 at 9:54
  • Of course, this example has no sense, this is just a simple example to show the problem. More generally, I have an class instance, and I want to get an attribute/call a method on this object. Commented Jun 5, 2019 at 9:57
  • You'll save a lot of time if you provide a minimal example that shows what you're actually trying to achieve, rather than a nonsense example. Sounds like this may be a duplicate of: stackoverflow.com/questions/46928713/… Commented Jun 5, 2019 at 10:07

3 Answers 3

1

Based on Steven's answer, I understood that shims-vue.d.ts is necessary to use component as typescript classes. But the problem is that they are all considered as Vue instances. This is obvious when looking at this file contents:

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

For now, the only clean way I found is to declare 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');
    });
});
Sign up to request clarification or add additional context in comments.

Comments

1

I noticed that Vue files are able to use other Vue files as their declared classes, which led me to try declaring the Jest files as Vue components as well. Surprisingly, it worked - no additional test-only interfaces required.

There are two steps. First, add the .vue suffix to Jest's test configuration in your package.json:

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

Second, rename your test files to .test.vue and wrap them in a <script> block:

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

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

// Tests here...
</script>

Now you can use wrapper.vm as the actual declared component class type, and Vetur/Typescript will be completely happy in both, the IDE and the compiler output.

Comments

1

Add these before your call.

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

You can also union the existing Test interface like this:

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

Not to say you should do this in practice, but your code will run...

The correct way to do fix this is to augment Vue's definition so typescript will accept your method. But that should be happening automatically by Vue. Are you including the shims-vue.d.ts file. That's where the typescript magic happens?

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

With that said, I have had issues using the Vue class syntax and have had to revert to oldschool syntax to avoid typescript complaining:

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

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

The shims file is how Vue augments itself with your components.

shims-vue.d.ts

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

Multiple Vue Instances

Sometimes Vue is included from multiple sources and this messes up typescript.

Try adding this to your tsconfig file.

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

I have sometimes even had to add webpack alias for this (this would be an issued building though, so not a fix for your issue):

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

8 Comments

Does not seem satisfying. I don't want to ignore error because I don't want test.unexistingMethod() to compile. About augmenting Vue's definition, it's not an option. Probably oldschool syntaxe would works, that makes me think it's a vue-class-component issue. I created an issue : github.com/vuejs/vue-class-component/issues/340
I've got shims-vue.d.ts, but not sure it is used. What should I do with this file?
Maybe your tsconfig file is excluding it.
It's not... but I'll look into that way
In fact, shims seems to declare all vue Components as Vue instances : github.com/vuejs/vue-class-component/issues/…
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.