# Use Symbol Injection Keys in Plugins ## Rule When using `provide/inject` in Vue plugins, use Symbol keys (preferably with `InjectionKey` for TypeScript) instead of string keys to prevent naming collisions and ensure type safety. ## Why This Matters 1. **Collision prevention**: String keys like `'i18n'` or `'api'` can easily collide between multiple plugins or different parts of your application. 2. **Type safety**: TypeScript's `InjectionKey` provides automatic type inference when using `inject()`. 3. **Refactoring safety**: Symbols are unique, so renaming is safe and explicit. 4. **Debugging**: Symbols can have descriptive names for debugging while remaining unique. ## Bad Practice ```typescript // plugin.ts export default { install(app) { // String key - can collide with other plugins! app.provide('http', axios) app.provide('config', appConfig) } } // component.vue const http = inject('http') // Type is unknown const config = inject('config') // Type is unknown // Another plugin accidentally uses the same key otherPlugin.install = (app) => { app.provide('http', differentHttpClient) // COLLISION! Overwrites first } ``` ## Good Practice ```typescript // plugins/keys.ts import type { InjectionKey } from 'vue' import type { AxiosInstance } from 'axios' export interface AppConfig { apiUrl: string timeout: number } // Typed injection keys - unique and type-safe export const httpKey: InjectionKey = Symbol('http') export const configKey: InjectionKey = Symbol('appConfig') // plugin.ts import { httpKey, configKey } from './keys' export default { install(app) { app.provide(httpKey, axios) app.provide(configKey, { apiUrl: '/api', timeout: 5000 }) } } // component.vue ``` ## Providing Default Values with Type Safety ```typescript // With InjectionKey, default values are type-checked const config = inject(configKey, { apiUrl: '/default-api', timeout: 3000 }) // Type: AppConfig (not undefined because default provided) // Type error if default doesn't match! const config = inject(configKey, { apiUrl: '/api' // Missing 'timeout' - TypeScript error! }) ``` ## Organizing Injection Keys For larger applications, organize keys by domain: ```typescript // injection-keys/index.ts export * from './auth' export * from './i18n' export * from './http' // injection-keys/auth.ts import type { InjectionKey } from 'vue' export interface AuthService { login: (credentials: Credentials) => Promise logout: () => Promise currentUser: Ref } export const authKey: InjectionKey = Symbol('auth') // injection-keys/i18n.ts export interface I18n { t: (key: string, params?: Record) => string locale: Ref } export const i18nKey: InjectionKey = Symbol('i18n') ``` ## Creating a useInject Helper For cleaner component code, create typed composables: ```typescript // composables/useAuth.ts import { inject } from 'vue' import { authKey, type AuthService } from '@/injection-keys' export function useAuth(): AuthService { const auth = inject(authKey) if (!auth) { throw new Error('Auth plugin not installed. Did you forget app.use(authPlugin)?') } return auth } // component.vue ``` ## When String Keys Are Acceptable 1. **Internal plugin use**: If both provide and inject are in the same plugin file 2. **Simple applications**: Very small apps with no collision risk 3. **Dynamic keys**: When the key name must be computed at runtime Even then, consider using a namespaced string: ```typescript // Better than plain 'config' app.provide('myPlugin:config', config) ``` ## References - [Vue.js Provide/Inject with Symbol Keys](https://vuejs.org/guide/components/provide-inject.html#working-with-symbol-keys) - [Vue.js TypeScript Support for Inject](https://vuejs.org/guide/typescript/composition-api.html#typing-provide-inject)