vue-class-component : TS2339 when calling class method

ぐ巨炮叔叔 提交于 2019-12-24 11:27:38

问题


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.


回答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://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'),



回答2:


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



回答3:


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.



来源:https://stackoverflow.com/questions/56457527/vue-class-component-ts2339-when-calling-class-method

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!