Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue 3 Inject with Typescript

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!

like image 325
Markus G. Avatar asked Jan 14 '21 10:01

Markus G.


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>
like image 51
Boussadjra Brahim Avatar answered Nov 12 '22 05:11

Boussadjra Brahim


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

like image 5
lucianokrebs Avatar answered Nov 12 '22 03:11

lucianokrebs