Vue 3 Inject with Typescript

Viewed 5659

I make use of the new Vue 3 Composition API and have written a "store" for reactive data.

const state = reactive<State>({
  accessToken: undefined,
  user: undefined,
});

export default {
  state: readonly(state),
}

On App creation I provide the store to all components:

const app = createApp(App)
  .provide("store", store)
  .use(IonicVue)
  .use(router);

And finally in a component / view I inject the store to make use of it.

export default defineComponent({
  name: "Home",
  inject: ["store"],
  components: {
    IonContent,
    IonHeader,
    IonPage,
    IonTitle,
    IonToolbar,
    IonButton,
  },
  computed: {
    email() {
      return this.store.state.user.email;
    },
  },
});
</script>

Unfortunately Typescript doesn't like the way I use this.store in the computed property email()

And says

Property 'store' does not exist on type 'ComponentPublicInstance<{}, {}, {}, { email(): any; }, {}, EmitsOptions, {}, {}, false, ComponentOptionsBase<{}, {}, {}, { email(): any; }, {}, ComponentOptionsMixin, ComponentOptionsMixin, EmitsOptions, string, {}>>'

I mean everything works fine when I remove the lang="ts" in the <script/> tag, but without the error is displayed. Any suggestions on how to fix this or what it particularly means?

Thanks in advance!

2 Answers

I recommend to use the store as a global property without specifying the inject in any child component because provide/inject could have some reactivity caveats:

const app = createApp(App)
  .use(IonicVue)
  .use(router);
app.config.globalProperties.store= store;

declare module '@vue/runtime-core' {
  interface ComponentCustomProperties  {
       store:any // replace it with the right type
     }
   }

then use it directly :

export default defineComponent({
  name: "Home",
  components: {
  ...
  },
  computed: {
    email() {
      return this.store.state.user.email;
    },
  },
});
</script>

For those who are dealing with the same issue with Vue 3 + TS, I found a solution without having to change the app.config or declare a new module:

  1. App.ts setup
import { createApp, reactive } from 'vue'
import App from './App.vue'

const Store = reactive({
  myProperty: 'my value'
})

createApp(App)
  .provide('Store', Store)
  .mount('#app')
  1. The component accessing the injected reactive object:
<template>
  <div>{{ Store.myProperty }}</div>
</template>

<script lang="ts">
import { IStore } from '@/types'
import { defineComponent, inject } from 'vue'

export default defineComponent({
  name: 'MyComponentName',
  setup() {
    return {
      Store: inject('Store') as IStore,
    }
  },
  created() {
    console.log(this.Store) // will show the `Store` in the console
  }
})
</script>
  1. The type defition for the Store (@/types.ts):
export interface IStore {
  myProperty: string
}

Following these 3 steps I was able to read/write to Store.myProperty without problems using TypeScript

Related