Home > OS >  Nuxt plugin property does not exist on type 'CombinedVueInstance
Nuxt plugin property does not exist on type 'CombinedVueInstance

Time:11-27

I've created a plugin and want to use it inside one of my components. The code from the plugin located under ~/plugins/socket.client.ts:

import { io } from 'socket.io-client'
import { Plugin } from '@nuxt/types'

const socketIOPlugin: Plugin = (_, inject) => {
  const socketUrl: string | undefined = process.env.socket_url

  if (!socketUrl || socketUrl.length <= 0) {
    throw new Error('socketUrl is undefined.')
  }

  const socket = io(socketUrl)
  inject('socket', socket)
}

export default socketIOPlugin

It is registered inside the nuxt.config.js. plugins: ['~/plugins/socket.client']

And I use it inside my component like this:

import Vue from 'vue'

export default Vue.extend({
  mounted() {
    this.$socket.on("example:hello", (data: any) => {
      console.log(data);
    });
  },
})

However, when I start nuxt, I get the following error: Property '$socket' does not exist on type 'CombinedVueInstance<Vue, unknown, unknown, unknown, Readonly<Record<never, any>>>'.

I've already tried adding a vue-shim.d.ts file, but still nothing changes.

declare module "*.vue" {
  import Vue from 'vue'
  export default Vue
}

declare module "vue/types/vue" {
  import { Socket } from "socket.io-client";
  interface Vue {
    $socket: Socket;
  }
}

I can't seem to find a way to solve this. Can anyone please help me?

CodePudding user response:

declare module "vue/types/vue" is a module augmentation - for augmentation to work correctly, it needs to be placed inside the TS file that contains at least one top-level import/export (more details here)

So my suggestion is to move that code from vue-shim.d.ts directly to socket.client.ts

CodePudding user response:

I've finally got it working by refactoring my code a bit.

Disclaimer; This solution works for me because it lets me use the plugin in another way. It is not specifically the answer to the question. The answer to this question can be achieved by using the plugin code from below and change the index.d.ts a bit so the $socket is also accessible inside the vue components. Read more about this here.

~/plugins/socket.client.ts

import { Plugin } from '@nuxt/types'
import { io, Socket } from 'socket.io-client'

function getSocketConnection(): Socket {
  const socketUrl: string | undefined = process.env.socket_url

  if (!socketUrl || socketUrl.length <= 0) {
    throw new Error('socketUrl is undefined.')
  }

  return io(socketUrl)
}

const socketIOPlugin: Plugin = (_, inject) => {
  inject('socket', getSocketConnection())
}

export default socketIOPlugin
export const socket = getSocketConnection()

I've also renamed vue-shim.d.ts to just index.d.ts and added the following lines:

import { accessorType } from '~/store'
import { socket } from '~/plugins/socket.client'

declare module 'vuex/types/index' {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  interface Store<S> {
    $socket: typeof socket
  }
}

declare module 'vue/types/vue' {
  interface Vue {
    $accessor: typeof accessorType
  }
}

declare module '@nuxt/types' {
  interface NuxtAppOptions {
    $accessor: typeof accessorType
  }
}

Instead of using the socket.io plugin inside my components, I've created a store ~/store/index.ts that handles all socket events.

import type { ActionTree, GetterTree, MutationTree } from 'vuex'
import { getAccessorType } from 'typed-vuex'

export const state = () => ({
  posts: [] as any[],
})

export type RootState = ReturnType<typeof state>

export const getters: GetterTree<RootState, RootState> = {
  posts: (state) => state.posts,
}

export const mutations: MutationTree<RootState> = {
  SET_POSTS: (state: RootState, posts: any[]) => (state.posts = posts),
}

export const actions: ActionTree<RootState, RootState> = {
  fetchPosts({ commit }) {
    this.$socket.emit('example:getPosts')
    this.$socket.once('example:posts', (data: any) => {
      commit('SET_POSTS', data)
    })
  },
}

export const accessorType = getAccessorType({
  state,
  getters,
  mutations,
  actions,
  modules: {},
})

The usage is now a lot easier and everything updates how it is ment to be with vuex.

~/pages/index.vue

<script lang='ts'>
import { Component, Vue } from 'nuxt-property-decorator'

@Component
export default class Index extends Vue {
  get posts() {
    return this.$accessor.posts
  }

  fetchPosts() {
    this.$accessor.fetchPosts()
  }
}
</script>

To get everything to work you need to install the following npm packages:

  • Related