'Vue 3 Inject Plugin with Composition API and TypeScript
I created a controller plugin to be used global in every component, but I can't make it work with Vue 3 + TypeScript + Composition API I get a TypeScript error
ui/plugins/controllers.ts
import { App } from 'vue'
import { provider, IProvider } from '@/core/presentation/provider'
export default {
install: (app: App) => {
const controllers: IProvider = provider()
app.provide('controllers', controllers)
}
}
main.ts
import { createApp } from 'vue'
import { controllers } from './ui'
createApp(App)
.use(controllers)
.mount('#app')
@/core/presentation/provider/provider.ts
import { UserController } from '../controllers'
import { IProvider } from './provider.types'
export const provider = (): IProvider => ({
users: new UserController()
})
ui/views/Component.vue
import { onMounted, ref, inject, defineComponent } from 'vue'
export default defineComponent({
setup() {
const controllers = inject('controllers')
const user = ref()
const getUser = async () => {
const result = await controllers.users.getById(1)
if (result) {
user.value = result.toJson()
}
}
onMounted(getUser)
return {
user,
getUser
}
}
})
Here I get a typescript error when I try to use the controller at this line
const result = await controllers.users.getById(1)
Error:
const controllers: unknown Object is of type 'unknown'.Vetur(2571)
If I set the type with my interface like this I get another typescript error
import { IProvider } from '@/core'
...
const controllers: IProvider = inject('controllers')
Error:
type 'IProvider | undefined' is not assignable to type 'IProvider'. Type 'undefined' is not assignable to type 'IProvider'.Vetur(2322)
I can only make it work like this but I think it's weird:
const controllers: IProvider | undefined = inject('controllers')
...
const result = await controllers?.users.getById(1)
Solution 1:[1]
I managed to solve my problem with post Type-safe Vue.js Injections
One thing to note is that the inject function produces the resolved type in union with
undefined. This is because there is the possibility that the injection is not resolved. It's up to you how you want to handle it.
So to deal with undefined I followed his advice and create an injectStrict function. Here is my final code:
Component.vue
import { IProvider } from '@/core'
import { injectStrict } from '@/ui/utils'
import { onMounted, ref, defineComponent } from 'vue'
export default defineComponent({
setup() {
const controllers: IProvider = injectStrict('controllers')
const user = ref()
const getUser = async () => {
const result = await controllers.users.getById(1)
if (result) {
user.value = result.toObj()
}
}
onMounted(getUser)
return {
user,
getUser
}
}
})
@/utils/injections.ts
import { inject } from 'vue'
function injectStrict<T>(key: string, fallback?: T) {
const resolved = inject(key, fallback)
if (!resolved) {
throw new Error(`Could not resolve ${key}`)
}
return resolved
}
export { injectStrict }
Solution 2:[2]
If you are sure that the injection is always resolved, you can use the Non-null assertion operator:
const controllers: IProvider = inject('controllers')! // <- notice the exclamation mark!
const result = await controllers.users.getById(1)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | ocordova |
| Solution 2 | Antonio Policastro |
