Vue 3 如何在组合式 API 中通过 provide/inject 实现类型安全的依赖注入

作者:袖梨 2026-08-25

关键在于用 InjectionKey 替代字符串 key,配合 TypeScript 泛型显式声明值的完整类型;必须用 Symbol 创建唯一键并泛型标注类型(如 Ref<User>),集中导出至 keys.ts;provide 时参数类型须与 key 泛型严格匹配。

关键在于用 InjectionKey 替代字符串 key,配合 TypeScript 泛型显式声明值的完整类型,从源头保证类型不丢失。

必须用 Symbol 声明类型化的 InjectionKey

字符串 key 会导致 inject 返回 unknownany,TypeScript 无法校验结构和响应式行为。正确做法是:

  1. Symbol() 创建唯一、不可冲突的键
  2. 通过泛型标注该键绑定值的确切类型,例如 Ref<User>ComputedRef<string>() => void
  3. 集中导出到 keys.ts,便于统一管理和跨组件复用

示例:

import type { InjectionKey, Ref } from 'vue'

export interface User { id: number; name: string }

export const userKey: InjectionKey> = Symbol()

export const logoutKey: InjectionKey void> = Symbol()

provide 时严格匹配 key 类型

provide 的第二个参数必须与 key 的泛型类型完全一致,否则 TypeScript 直接报错,杜绝“绕过检查”的断言写法:

  1. 若 key 是 InjectionKey,则必须传入 ref<User>(...)
  2. 提供默认值时,也要满足相同类型约束,比如 ref({ id: 0, name: '' })
  3. 全局注入(app.provide())同样支持 InjectionKey,类型规则一致

示例:

import { ref, provide } from 'vue'

import { userKey } from './keys'

const user = ref({ id: 1, name: 'Alice' })

provide(userKey, user) // 类型匹配,无错误

inject 后自动获得完整类型推导

使用类型化 key 调用 inject,返回值即为精确类型(如 Ref<User> | undefined),无需手动断言或泛型标注:

  1. 可直接访问 .value、参与 watch、解构属性,IDE 正确提示
  2. 带默认值时,类型仍收敛,例如 inject(userKey, ref({ id: 0, name: '' }))
  3. 若用工厂函数作默认值,需显式传入 true 第三个参数,否则 TS 会误判为函数类型

示例:

import { inject } from 'vue'

import { userKey } from './keys'

const user = inject(userKey) // 类型:Ref | undefined

if (user) { console.log(user.value.name) } // 安全访问

封装成组合式函数提升可维护性

把 provide/inject 逻辑收进自定义 Hook,隐藏底层细节,暴露语义化 API:

  1. useAuthProvider() 封装登录态提供逻辑
  2. useTheme() 封装主题配置的注入与更新方法
  3. 内部仍基于 InjectionKey,外部调用更简洁、类型依然完整

这样既保持类型安全,又避免重复声明 key 和样板代码,适合中大型项目长期维护。

相关文章

精彩推荐