vue-class-component : 调用类方法时 TS2339

Posted

技术标签:

【中文标题】vue-class-component : 调用类方法时 TS2339【英文标题】:vue-class-component : TS2339 when calling class method 【发布时间】:2019-10-20 18:56:36 【问题描述】:

我正在使用 vue-cli-service 来构建我的 vuejs 应用程序。

构建成功,但在 webstorm IDE 中,我得到一些 TS2339 错误:

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

在 Test.spec.ts 中,我在编辑器和打字稿窗口中都收到此错误:

错误:(14, 21) TS2339:“Vue”类型上不存在属性“方法”。

但是测试没问题,所以test.method()在运行时解析成功。

【问题讨论】:

您是否缺少类型定义? 类定义不应该足够吗?您会在此处添加哪些额外定义? 你想用 'const value = new Test().value' 达到什么目的?看起来您正在尝试注入依赖项或在本地注册组件,但都没有按照您尝试的方式完成。 当然,这个例子没有任何意义,这只是一个简单的例子来说明问题。更一般地说,我有一个类实例,我想在这个对象上获取一个属性/调用一个方法。 如果您提供一个显示您实际尝试实现的目标的最小示例,而不是无意义的示例,您将节省大量时间。听起来像这样可能是重复的:***.com/questions/46928713/… 【参考方案1】:

在通话前添加这些内容。

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

你也可以像这样联合现有的Test接口:

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

并不是说你应该在实践中这样做,但你的代码会运行......

解决此问题的正确方法是augment Vue's 定义,因此typescript 将接受您的方法。但这应该由 Vue 自动发生。 您是否包含shims-vue.d.ts 文件。这就是打字稿魔法发生的地方?

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

话虽如此,我在使用 Vue 类语法时遇到了问题,不得不恢复到老式语法以避免打字稿抱怨:

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

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

shims 文件是 Vue 使用您的组件增强自身的方式。

shims-vue.d.ts

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

多个 Vue 实例

有时Vue 包含在多个来源中,这会弄乱打字稿。

尝试将此添加到您的 tsconfig 文件中。


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

有时我什至不得不为此添加 webpack 别名(不过,这将是一个已发布的建筑物,因此不能解决您的问题):

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

【讨论】:

似乎并不令人满意。我不想忽略错误,因为我不想编译 test.unexistingMethod()。关于扩充 Vue 的定义,这不是一个选项。可能老式语法会起作用,这让我认为这是一个 vue-class-component 问题。我创建了一个问题:github.com/vuejs/vue-class-component/issues/340 我有 shims-vue.d.ts,但不确定是否使用过。我应该如何处理这个文件? 也许你的 tsconfig 文件不包括它。 不是……但我会调查一下 事实上,shims 似乎将所有 vue 组件声明为 Vue 实例:github.com/vuejs/vue-class-component/issues/…【参考方案2】:

根据 Steven 的回答,我了解到 shims-vue.d.ts 是使用组件作为打字稿类所必需的。但问题是它们都被视为 Vue 实例。 查看此文件内容时,这一点很明显:

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

目前,我发现的唯一干净的方法是声明一个由我的组件实现的接口:

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】:

我注意到 Vue 文件能够使用其他 Vue 文件作为其声明的类,这促使我尝试将 Jest 文件也声明为 Vue 组件。令人惊讶的是,它有效 - 不需要额外的仅测试接口。

有两个步骤。首先,在您的package.json 中将.vue 后缀添加到Jest 的测试配置中:


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

其次,将您的测试文件重命名为 .test.vue 并将它们包装在 &lt;script&gt; 块中:

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

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

// Tests here...
</script>

现在您可以使用wrapper.vm 作为实际声明的组件类类型,Vetur/Typescript 将在 IDE 和编译器输出中完全满意。

【讨论】:

以上是关于vue-class-component : 调用类方法时 TS2339的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 vue-class-component 使用 Vue Router 延迟加载?

vue-class-component

vue-class-component:如何创建和初始化反射数据

Vuelidate 与 Vue 3 + vue-class-component + TypeScript

vue-class-component 以class的模式写vue组件

[Vue @Component] Simplify Vue Components with vue-class-component