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
相关产品推荐
相关产品推荐

