You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue.js 2.6.14中Vetur报2349错误:表达式不可调用,代码可运行

Vue 2.6.14 + TypeScript Mixin 调用报错:This expression is not callable(Vetur 2349)

问题场景

开发Vue.js 2.6.14组件时,通过Mixin实现数字转价格格式的功能,代码实际运行正常,但Vetur(错误码2349)提示“This expression is not callable”,无法定位问题根源。

组件代码

<template>
  <p class="mb-0">
    <span class="text-alpha-80 fw-300">{{ fullPrice }}</span>
    <span class="text-alpha-30 fw-500">{{ price.currency }}</span>
  </p>
</template>

<script lang="ts">
import Vue from 'vue'
import currencyMixin from '@/mixins/currencyMixin'

export default Vue.extend({
  name: 'PriceCard',
  mixins: [currencyMixin],
  props: {
    price: {
      type: Object,
      default: () => {},
      required: true
    }
  },
  computed: {
    fullPrice(): string {
      const price = this.formatCurrency(this.price.value)
      return price
    }
  }
})
</script>

Mixin代码

import Vue from 'vue'

const currencyMixin = Vue.extend({
  methods: {
    $_getThousands(i: string, j: number, thousands: string): string {
      return (j ? i.substr(0, j) + thousands : '')
    },

    $_getAfterThousands(i: string, j: number, thousands: string): string {
      return i.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thousands)
    },

    $_getDecimals(amount: number, i: string, decimalCount: number, decimal: string = ','): string {
      const aux = Math.abs(amount - parseInt(i)).toFixed(decimalCount).slice(2)
      return decimalCount ? decimal + aux : ''
    },

    formatCurrency(amount: number, decimalCount: number = 0, decimal: string = ',', thousands: string = '.'): string {
      const sign = '$ '
      const stringAmount: string = Math.abs(amount || 0).toFixed(decimalCount)
      const i: string = parseInt((stringAmount)).toString()
      const j: number = i.length > 3 ? i.length % 3 : 0

      return (
        sign +
        this.$_getThousands(i, j, thousands) +
        this.$_getAfterThousands(i, j, thousands) +
        this.$_getDecimals(amount, i, decimalCount, decimal)
      )
    },

  },
})

export default currencyMixin

问题原因

这是Vue 2 + TypeScript组合下的类型推断缺陷:用Vue.extend()创建的Mixin,其方法无法被TypeScript自动识别为组件实例上的可调用函数,导致Vetur的类型检查报错。本质是Vue 2的类型系统在Mixin合并时,无法正确推导Mixin方法的类型到组件实例上。

解决方案

方案1:类型断言快速修复

在组件中调用Mixin方法时,通过类型断言明确this包含Mixin的方法类型:

computed: {
  fullPrice(): string {
    // 断言this包含formatCurrency方法
    const vm = this as Vue & { formatCurrency: typeof currencyMixin.options.methods.formatCurrency }
    const price = vm.formatCurrency(this.price.value)
    return price
  }
}

方案2:规范Mixin类型定义

改用Vue提供的Mixin类型工具定义Mixin,替代Vue.extend(),让TypeScript能正确识别Mixin的方法类型:

// 修改后的Mixin代码
import Vue, { Mixin } from 'vue'

const currencyMixin: Mixin<Vue> = {
  methods: {
    $_getThousands(i: string, j: number, thousands: string): string {
      return (j ? i.substr(0, j) + thousands : '')
    },

    $_getAfterThousands(i: string, j: number, thousands: string): string {
      return i.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thousands)
    },

    $_getDecimals(amount: number, i: string, decimalCount: number, decimal: string = ','): string {
      const aux = Math.abs(amount - parseInt(i)).toFixed(decimalCount).slice(2)
      return decimalCount ? decimal + aux : ''
    },

    formatCurrency(amount: number, decimalCount: number = 0, decimal: string = ',', thousands: string = '.'): string {
      const sign = '$ '
      const stringAmount: string = Math.abs(amount || 0).toFixed(decimalCount)
      const i: string = parseInt((stringAmount)).toString()
      const j: number = i.length > 3 ? i.length % 3 : 0

      return (
        sign +
        this.$_getThousands(i, j, thousands) +
        this.$_getAfterThousands(i, j, thousands) +
        this.$_getDecimals(amount, i, decimalCount, decimal)
      )
    },
  },
}

export default currencyMixin

方案3:使用Vue Class Component(类组件场景)

如果项目采用Vue Class Component开发,用Mixins装饰器引入Mixin,类型会自动合并,无需额外处理:

// 修改后的组件代码
import { Component, Vue, Mixins, Prop } from 'vue-property-decorator'
import currencyMixin from '@/mixins/currencyMixin'

@Component
export default class PriceCard extends Mixins(currencyMixin) {
  @Prop({ required: true }) price!: { value: number; currency: string }

  get fullPrice(): string {
    return this.formatCurrency(this.price.value)
  }
}

内容的提问来源于stack exchange,提问作者Numa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 05:40:21