Vue 3 使用 Composition API 和 TypeScript 注入插件

Vue 3 Inject Plugin with Composition API and TypeScript

我创建了一个 controller 插件以在每个组件中全局使用,但我无法使其与 Vue 3 + TypeScript + Composition 一起使用 API 我收到 TypeScript 错误

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
    }
  }
})

当我尝试在此行使用控制器时出现打字错误

const result = await controllers.users.getById(1)

错误:

const controllers: unknown
Object is of type 'unknown'.Vetur(2571)

如果我像这样用我的界面设置类型,我会得到另一个打字稿错误

import { IProvider } from '@/core'
...
const controllers: IProvider  = inject('controllers')

错误:

type 'IProvider | undefined' is not assignable to type 'IProvider'.
Type 'undefined' is not assignable to type 'IProvider'.Vetur(2322)

我只能让它这样工作,但我觉得它很奇怪:

const controllers: IProvider | undefined = inject('controllers')
...
const result = await controllers?.users.getById(1)

我设法用 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.

所以为了处理 undefined 我听从了他的建议并创建了一个 injectStrict 函数。这是我的最终代码:

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 }

如果你确定注入总能解决,可以使用Non-null assertion operator:

const controllers: IProvider = inject('controllers')! // <- notice the exclamation mark!
const result = await controllers.users.getById(1)