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

Nuxt中使用TypeScript装饰器报错的解决方法咨询

Nuxt中使用TypeScript装饰器出现语法错误的解决方法

背景

我在Nuxt应用的/service目录下创建服务层,通过类(比如LocationService)管理后端API调用(查询、创建、删除地点)。为了使用这些服务,我给它们提供store、useNotification或useToast等上下文对象,封装成composable。

为了让代码更整洁有序,我尝试添加装饰器来为函数扩展额外功能。

示例代码

比如我想为每个API调用添加加载状态:

import {ToastService} from "./useToastService";
import {WithLoading} from "./with-loading.decorator";
import {RestClient} from "@ayu-sh-kr/dota-rest";

export class LocationService {

  restClient!: RestClient;

  constructor(public toast: ToastService) {
  }

  @WithLoading()
  async get() {
     return await this.restClient.get<Location[]>()
            .uri('/location/v1')
            .retrieve()
            .toEntity();
  }

}

目前@WithLoading装饰器还没实现功能,只是打算通过LoadingStore管理加载状态,但使用后出现500错误及未识别令牌报错。

错误信息

@__vite_ssr_import_2__.WithLoading()
  ^

  SyntaxError: Invalid or unexpected token
  at Script (node:vm:116:7)
  at createScript (node:vm:268:10)
  at Object.runInThisContext (node:vm:316:10)
  at ViteNodeRunner.runModule (/D:/projects/fastrack/fasttrack-frontend/node_modules/.pnpm/vite-node@2.1.8_@types+node@22.10.1_terser@5.37.0/node_modules/vite-node/dist/client.mjs:398:19)
  at ViteNodeRunner.directRequest (/D:/projects/fastrack/fasttrack-frontend/node_modules/.pnpm/vite-node@2.1.8_@types+node@22.10.1_terser@5.37.0/node_modules/vite-node/dist/client.mjs:381:16)
  at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
  at async ViteNodeRunner.cachedRequest (/D:/projects/fastrack/fasttrack-frontend/node_modules/.pnpm/vite-node@2.1.8_@types+node@22.10.1_terser@5.37.0/node_modules/vite-node/dist/client.mjs:206:14)
  at async ViteNodeRunner.dependencyRequest (/D:/projects/fastrack/fasttrack-frontend/node_modules/.pnpm/vite-node@2.1.8_@types+node@22.10.1_terser@5.37.0/node_modules/vite-node/dist/client.mjs:259:12)
  at async components\route\location\LocationTable.vue:9:31
  at async ViteNodeRunner.runModule (/D:/projects/fastrack/fasttrack-frontend/node_modules/.pnpm/vite-node@2.1.8_@types+node@22.10.1_terser@5.37.0/node_modules/vite-node/dist/client.mjs:399:5)

问题在于,尽管我已经在tsconfig.json中启用了装饰器支持,Nuxt仍然无法处理装饰器。

当前配置

tsconfig.json

{
  // https://nuxt.com/docs/guide/concepts/typescript
  "extends": "./.nuxt/tsconfig.json",
  "compilerOptions": {
    "experimentalDecorators": true, // Enable decorator support
    "emitDecoratorMetadata": true, // Required for metadata reflection with decorators
    "target": "esnext", // Use "esnext" or a compatible ES version
    "module": "esnext" // Ensure modern module support
  }
}

nuxt.config.ts

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  compatibilityDate: '2024-11-01',
  devtools: {enabled: true},
  css: ['~/assets/css/main.css'],

  postcss: {
    plugins: {
      tailwindcss: {},
      autoprefixer: {},
    },
  },
  ssr: true,
  modules: ['@nuxt/ui', '@pinia/nuxt'],
  runtimeConfig: {
    public: {
      BACKEND_URL: process.env.NUXT_PUBLIC_BACKEND_URL
    }
  }
})

解决方法

  1. 安装必要的Vite插件
    Nuxt默认编译链需要额外插件处理装饰器语法,安装@vitejs/plugin-react(即使使用Vue,该插件也能兼容处理TypeScript装饰器):
pnpm add -D @vitejs/plugin-react
  1. 配置Nuxt的Vite插件
    修改nuxt.config.ts,添加Vite插件及装饰器相关Babel配置:
import react from '@vitejs/plugin-react'

export default defineNuxtConfig({
  // ...其他原有配置
  vite: {
    plugins: [
      react({
        babel: {
          plugins: [
            ['@babel/plugin-proposal-decorators', { legacy: true }],
            'babel-plugin-transform-typescript-metadata'
          ]
        }
      })
    ]
  }
})

这里legacy: true需要和tsconfig.json中的experimentalDecorators配置匹配,确保语法兼容。

  1. 检查装饰器实现规范
    确保WithLoading装饰器符合TypeScript函数式装饰器的写法,示例实现如下:
// with-loading.decorator.ts
import { useLoadingStore } from '~/stores/loading'

export function WithLoading() {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value
    descriptor.value = async function (...args: any[]) {
      const loadingStore = useLoadingStore()
      loadingStore.startLoading(propertyKey)
      try {
        return await originalMethod.apply(this, args)
      } finally {
        loadingStore.stopLoading(propertyKey)
      }
    }
    return descriptor
  }
}
  1. 重启开发服务
    修改配置后必须重启Nuxt开发服务器,确保新配置生效。

  2. 备选方案:改用组合式函数替代装饰器
    如果装饰器配置仍有问题,可以直接用组合式函数包裹API调用,绕开编译问题:

// useLoadingWrapper.ts
import { useLoadingStore } from '~/stores/loading'

export async function withLoading<T>(key: string, fn: () => Promise<T>): Promise<T> {
  const loadingStore = useLoadingStore()
  loadingStore.startLoading(key)
  try {
    return await fn()
  } finally {
    loadingStore.stopLoading(key)
  }
}

在服务类中使用:

async get() {
  return await withLoading('location:get', () => 
    this.restClient.get<Location[]>()
      .uri('/location/v1')
      .retrieve()
      .toEntity()
  )
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 23:19:56