关键在于用 InjectionKey 替代字符串 key,配合 TypeScript 泛型显式声明值的完整类型;必须用 Symbol 创建唯一键并泛型标注类型(如 Ref<User>),集中导出至 keys.ts;provide 时参数类型须与 key 泛型严格匹配。
关键在于用 InjectionKey 替代字符串 key,配合 TypeScript 泛型显式声明值的完整类型,从源头保证类型不丢失。
字符串 key 会导致 inject 返回 unknown 或 any,TypeScript 无法校验结构和响应式行为。正确做法是:
Symbol() 创建唯一、不可冲突的键Ref<User>、ComputedRef<string>、() => void
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 的泛型类型完全一致,否则 TypeScript 直接报错,杜绝“绕过检查”的断言写法:
InjectionKey,则必须传入 ref<User>(...)
ref({ id: 0, name: '' })
app.provide())同样支持 InjectionKey,类型规则一致示例:
import { ref, provide } from 'vue'
import { userKey } from './keys'
const user = ref({ id: 1, name: 'Alice' })
provide(userKey, user) // 类型匹配,无错误
使用类型化 key 调用 inject,返回值即为精确类型(如 Ref<User> | undefined),无需手动断言或泛型标注:
.value、参与 watch、解构属性,IDE 正确提示inject(userKey, ref({ id: 0, name: '' }))
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:
useAuthProvider() 封装登录态提供逻辑useTheme() 封装主题配置的注入与更新方法这样既保持类型安全,又避免重复声明 key 和样板代码,适合中大型项目长期维护。