| |

Vue.js 39 🟢 Type-Safe Injection Keys using InjectionKey Symbols

The provide() and the inject() pair makes the value available deep in the tree, but the key is a string by default, and the string is not the type-safe. The inject('theme') returns the unknown, and the TypeScript cannot check the type. The developer must cast the value, and the cast is the runtime risk. The InjectionKey<T> is the Vue’s solution: the typed symbol that carries the value type in the TypeScript. The InjectionKey<ThemeContext> declares the key and the value type, and the provide(key, value) and the inject(key) are the type-checked. The compiler catches the wrong type, and the value is the typed without the cast. The typed key is the foundation of the type-safe dependency injection in the Vue 3 and the TypeScript.

The InjectionKey is the interface that extends the Symbol. The const ThemeKey: InjectionKey<ThemeContext> = Symbol('theme') creates the key with the type parameter. The provide(ThemeKey, themeContext) requires the themeContext to match the ThemeContext type, and the inject(ThemeKey) returns the ThemeContext | undefined. The type parameter is the contract: the provider and the injector must agree on the shape, and the compiler enforces the agreement.

The typed key is especially valuable in the shared libraries. The library exports the InjectionKey and the type, and the consumer imports the key and the type. The consumer’s provide and inject are the type-checked against the library’s contract, and the mismatch is the compile error. The useX() composable wraps the inject() and throws when the key is missing, and the return type is the typed. The library’s API is the typed, and the consumer’s usage is the safe.

This chapter covers three areas. First, why the typed injection keys exist — the string-key problem and the type-safety. Second, the InjectionKey syntax — the declaration, the type parameter, the provide and the inject with the typed key, and the default value. Third, the patterns and the pitfalls — the library API, the composable wrapper, the multiple keys, the type inference, and the common mistakes. The chapter ends with a complete example session, a quick reference, best practices, common pitfalls, real-world examples, and diagrams showing the typed injection flow.

Key point: The InjectionKey<T> is the typed symbol that carries the value type. The const Key: InjectionKey<T> = Symbol('key') declares the key and the type. The provide(Key, value) requires the value to match the type, and the inject(Key) returns the typed value or the undefined. The InjectionKey makes the provide and the inject the type-safe, and the shared libraries use it to export the typed API.


Why the typed injection keys exist

The string-key problem. The inject('theme') with the string key returns the unknown. The TypeScript cannot check the type, and the developer must cast the value. The cast is the runtime risk: the wrong cast is the runtime error, and the compiler does not catch it. The string key is the convenient and the unsafe.

The type-safety problem. The provide and the inject are the separate files. The provider defines the value, and the injector reads it. The two must agree on the type, and the string key does not enforce the agreement. The InjectionKey<T> carries the type, and the compiler checks the provider and the injector against the same T. The mismatch is the compile error.

The contract problem. The library exports the key and the type, and the consumer imports them. The key is the token, and the type is the shape. The consumer’s provide and inject are the type-checked against the library’s contract, and the wrong value is the compile error. The contract is the explicit, and the InjectionKey is the carrier.

The refactor problem. The string keys are the typo-prone. The inject('theme') and the inject('themes') are the different keys, and the typo is the silent undefined. The InjectionKey is the variable, and the typo is the compile error. The rename is the safe, because the IDE renames the variable and the imports.

The Symbol problem. The string keys are the global, and the two libraries can use the same string. The Symbol is the unique, and the InjectionKey extends the Symbol. The collision is the impossible, and the typed key is the unique and the typed.

The trade-off. The InjectionKey adds the boilerplate: the declaration, the type, and the import. The string key is the shorter, and the typed key is the safer. The trade-off is between the brevity of the string and the type-safety of the InjectionKey, and the TypeScript projects choose the InjectionKey.


a. The InjectionKey declaration and usage

The InjectionKey is imported from the vue and declared with the type parameter:

import type { InjectionKey } from 'vue';

interface ThemeContext {
  theme: string;
  toggleTheme: () => void;
}

const ThemeKey: InjectionKey<ThemeContext> = Symbol('theme');

The InjectionKey<ThemeContext> declares the key with the type, and the Symbol('theme') creates the runtime symbol. The ThemeKey is the typed key, and the ThemeContext is the value type.

The provider uses the key:

import { provide, ref, readonly } from 'vue';
import { ThemeKey } from './keys';

const theme = ref('light');

function toggleTheme() {
  theme.value = theme.value === 'light' ? 'dark' : 'light';
}

provide(ThemeKey, {
  theme: theme.value,
  toggleTheme,
});

The provide(ThemeKey, value) requires the value to match the ThemeContext type. The wrong value is the compile error.

The injector uses the key:

import { inject } from 'vue';
import { ThemeKey } from './keys';

const themeContext = inject(ThemeKey);

// The type is ThemeContext | undefined.
if (!themeContext) {
  throw new Error('The ThemeKey is missing');
}

// The type is ThemeContext.
themeContext.toggleTheme();

The inject(ThemeKey) returns the ThemeContext | undefined. The undefined is the possible when no provider exists, and the check narrows the type.

The default value. The inject() with the default returns the non-undefined type:

const themeContext = inject(ThemeKey, {
  theme: 'light',
  toggleTheme: () => {},
});

// The type is ThemeContext (the not undefined).

The default is the ThemeContext, and the return type is the non-undefined. The default eliminates the undefined check, but it hides the missing provider.

The factory default. The inject() with the factory:

const themeContext = inject(ThemeKey, () => ({
  theme: 'light',
  toggleTheme: () => {},
}));

The factory is the lazy, and the return type is the non-undefined.

The readonly. The readonly wraps the state, and the type is the Readonly<Ref<T>>:

import { provide, ref, readonly } from 'vue';

const theme = ref('light');

provide(ThemeKey, {
  theme: readonly(theme),
  toggleTheme,
});

The theme in the context is the Readonly<Ref<string>>, and the descendants cannot mutate it.


b. The library API and the composable

The library’s keys file. The library exports the keys and the types:

// the-library/keys.ts
import type { InjectionKey, Ref } from 'vue';

export interface ThemeContext {
  theme: Readonly<Ref<string>>;
  toggleTheme: () => void;
}

export const ThemeKey: InjectionKey<ThemeContext> = Symbol('theme');

The ThemeKey is the typed, and the ThemeContext is the exported type. The consumer imports both.

The library’s composable. The library provides the useX() composable that wraps the inject() and throws when the key is missing:

// the-library/composables.ts
import { inject } from 'vue';
import { ThemeKey, type ThemeContext } from './keys';

export function useTheme(): ThemeContext {
  const context = inject(ThemeKey);
  if (!context) {
    throw new Error('The useTheme must be used within the ThemeProvider');
  }
  return context;
}

The useTheme() returns the ThemeContext, and the undefined is the handled. The consumer does not check the undefined.

The consumer’s provider. The consumer’s provider uses the key:

// the-app/ThemeProvider.vue
<script setup lang="ts">
import { provide, ref, readonly } from 'vue';
import { ThemeKey } from 'the-library';

const theme = ref('light');

function toggleTheme() {
  theme.value = theme.value === 'light' ? 'dark' : 'light';
}

provide(ThemeKey, {
  theme: readonly(theme),
  toggleTheme,
});
</script>

<template>
  <slot />
</template>

The provide(ThemeKey, value) is the type-checked, and the theme is the Readonly<Ref<string>>.

The consumer’s injector. The consumer’s component uses the useTheme():

<!-- the-app/ThemeToggle.vue -->
<script setup lang="ts">
import { useTheme } from 'the-library';

const { theme, toggleTheme } = useTheme();
</script>

<template>
  <button @click="toggleTheme">
    The theme is {{ theme }}
  </button>
</template>

The useTheme() is the typed, and the theme is the Readonly<Ref<string>>. The template unwraps the ref.

The app.provide(). The application-level provide with the typed key:

import { createApp } from 'vue';
import { ThemeKey } from 'the-library';

const app = createApp(App);

app.provide(ThemeKey, {
  theme: readonly(ref('light')),
  toggleTheme: () => {},
});

The app.provide(ThemeKey, value) is the type-checked, and the value must match the ThemeContext.


c. The patterns and the pitfalls

The multiple keys. The library can export the multiple typed keys:

// the-library/keys.ts
export const ThemeKey: InjectionKey<ThemeContext> = Symbol('theme');
export const AuthKey: InjectionKey<AuthContext> = Symbol('auth');
export const I18nKey: InjectionKey<I18nContext> = Symbol('i18n');

The consumer imports the specific key, and the types are the separate.

The type inference. The InjectionKey<T> carries the type, and the inject(Key) infers the T | undefined:

const themeContext = inject(ThemeKey);
// The type: ThemeContext | undefined

The TypeScript infers the type from the key, and the cast is the unnecessary.

The InjectionKey with the ComputedRef. The provided value can be the computed:

interface CounterContext {
  count: ComputedRef<number>;
  increment: () => void;
}

const CounterKey: InjectionKey<CounterContext> = Symbol('counter');

provide(CounterKey, {
  count: computed(() => count.value * 2),
  increment,
});

The ComputedRef is the readonly by the nature, and the consumer reads the .value.

The InjectionKey with the Ref. The provided value can be the ref:

interface CounterContext {
  count: Ref<number>;
  increment: () => void;
}

const CounterKey: InjectionKey<CounterContext> = Symbol('counter');

provide(CounterKey, {
  count,
  increment,
});

The Ref is the mutable, and the consumer can mutate it. The readonly(count) is the safer.

The wrong type. The provide(ThemeKey, wrongValue) is the compile error:

// ❌ The wrong type
provide(ThemeKey, {
  theme: 'light',  // the should be the Ref<string>
  toggleTheme,
});

The compiler checks the value against the ThemeContext, and the wrong shape is the error.

The missing check. The inject(ThemeKey) returns the undefined when the provider is missing:

// ❌ The no check
const themeContext = inject(ThemeKey);
themeContext.toggleTheme();  // the runtime error if undefined

// ✅ The check
const themeContext = inject(ThemeKey);
if (!themeContext) throw new Error('The ThemeKey is missing');
themeContext.toggleTheme();

The undefined check is the requirement, or the useX() composable handles it.

The Symbol description. The Symbol('theme') description is the debugging help, and the two Symbol('theme') are the different symbols:

const Key1 = Symbol('theme');
const Key2 = Symbol('theme');
console.log(Key1 === Key2);  // false

The Symbol is the unique, and the description is the debugging.

The InjectionKey in the JavaScript. The InjectionKey is the TypeScript-only, and the JavaScript uses the string or the Symbol:

// The JavaScript
const ThemeKey = Symbol('theme');
provide(ThemeKey, theme);
const theme = inject(ThemeKey);

The InjectionKey is the type annotation, and the runtime is the Symbol.


Complete Example Session

// ============================================
// PART 1: THE TYPED KEY DECLARATION
// ============================================

import type { InjectionKey, Ref } from 'vue';

interface ThemeContext {
  theme: Readonly<Ref<string>>;
  toggleTheme: () => void;
}

const ThemeKey: InjectionKey<ThemeContext> = Symbol('theme');


// ============================================
// PART 2: THE PROVIDER
// ============================================

import { provide, ref, readonly } from 'vue';
import { ThemeKey } from './keys';

const theme = ref('light');

function toggleTheme() {
  theme.value = theme.value === 'light' ? 'dark' : 'light';
}

provide(ThemeKey, {
  theme: readonly(theme),
  toggleTheme,
});


// ============================================
// PART 3: THE INJECTOR WITH THE CHECK
// ============================================

import { inject } from 'vue';
import { ThemeKey } from './keys';

const themeContext = inject(ThemeKey);

if (!themeContext) {
  throw new Error('The ThemeKey is missing');
}

themeContext.toggleTheme();


// ============================================
// PART 4: THE COMPOSABLE WRAPPER
// ============================================

import { inject } from 'vue';
import { ThemeKey, type ThemeContext } from './keys';

export function useTheme(): ThemeContext {
  const context = inject(ThemeKey);
  if (!context) {
    throw new Error('The useTheme must be used within the ThemeProvider');
  }
  return context;
}


// ============================================
// PART 5: THE LIBRARY'S KEYS FILE
// ============================================

// the-library/keys.ts
import type { InjectionKey, Ref } from 'vue';

export interface ThemeContext {
  theme: Readonly<Ref<string>>;
  toggleTheme: () => void;
}

export const ThemeKey: InjectionKey<ThemeContext> = Symbol('theme');


// ============================================
// PART 6: THE CONSUMER'S PROVIDER
// ============================================

<script setup lang="ts">
import { provide, ref, readonly } from 'vue';
import { ThemeKey } from 'the-library';

const theme = ref('light');

function toggleTheme() {
  theme.value = theme.value === 'light' ? 'dark' : 'light';
}

provide(ThemeKey, {
  theme: readonly(theme),
  toggleTheme,
});
</script>

<template>
  <slot />
</template>


// ============================================
// PART 7: THE CONSUMER'S INJECTOR
// ============================================

<script setup lang="ts">
import { useTheme } from 'the-library';

const { theme, toggleTheme } = useTheme();
</script>

<template>
  <button @click="toggleTheme">
    The theme is {{ theme }}
  </button>
</template>


// ============================================
// PART 8: THE MULTIPLE KEYS
// ============================================

// the-library/keys.ts
export const ThemeKey: InjectionKey<ThemeContext> = Symbol('theme');
export const AuthKey: InjectionKey<AuthContext> = Symbol('auth');
export const I18nKey: InjectionKey<I18nContext> = Symbol('i18n');


// ============================================
// PART 9: THE APP.PROVIDE
// ============================================

import { createApp, ref, readonly } from 'vue';
import { ThemeKey } from 'the-library';

const app = createApp(App);

app.provide(ThemeKey, {
  theme: readonly(ref('light')),
  toggleTheme: () => {},
});


// ============================================
// PART 10: THE COMPLETE EXAMPLE
// ============================================

// the-library/keys.ts
import type { InjectionKey, Ref } from 'vue';

export interface ThemeContext {
  theme: Readonly<Ref<string>>;
  toggleTheme: () => void;
}

export const ThemeKey: InjectionKey<ThemeContext> = Symbol('theme');

// the-library/composables.ts
import { inject } from 'vue';
import { ThemeKey, type ThemeContext } from './keys';

export function useTheme(): ThemeContext {
  const context = inject(ThemeKey);
  if (!context) {
    throw new Error('The useTheme must be used within the ThemeProvider');
  }
  return context;
}

// the-app/ThemeProvider.vue
<script setup lang="ts">
import { provide, ref, readonly } from 'vue';
import { ThemeKey } from 'the-library';

const theme = ref('light');

function toggleTheme() {
  theme.value = theme.value === 'light' ? 'dark' : 'light';
}

provide(ThemeKey, {
  theme: readonly(theme),
  toggleTheme,
});
</script>

<template>
  <slot />
</template>

// the-app/ThemeToggle.vue
<script setup lang="ts">
import { useTheme } from 'the-library';

const { theme, toggleTheme } = useTheme();
</script>

<template>
  <button @click="toggleTheme">
    The theme is {{ theme }}
  </button>
</template>

The ten parts show the typed key declaration, the provider, the injector with the check, the composable wrapper, the library’s keys file, the consumer’s provider, the consumer’s injector, the multiple keys, the app.provide(), and the complete example.


Quick Reference

The InjectionKey Declaration

The partThe example
The importimport type { InjectionKey } from 'vue'
The declarationconst Key: InjectionKey<T> = Symbol('key')
The typeThe interface T { ... }
The runtimeThe Symbol('key')

The provide and the inject

The functionThe type
provide(Key, value)The value must match the T
inject(Key)The T | undefined
inject(Key, default)The T
inject(Key, () => default)The T

The Type Inference

The expressionThe type
inject(ThemeKey)The ThemeContext | undefined
inject(ThemeKey, default)The ThemeContext
provide(ThemeKey, value)The value must be the ThemeContext

The Library API

The fileThe content
The keys.tsThe InjectionKey<T> and the interface T
The composables.tsThe useX() that wraps the inject()
The index.tsThe re-exports

The Common Types

The typeThe use
Ref<T>The mutable
Readonly<Ref<T>>The readonly
ComputedRef<T>The computed
() => voidThe function

Best Practices

✅ Do This:

// Use the InjectionKey for the type safety
const ThemeKey: InjectionKey<ThemeContext> = Symbol('theme');             // ✅
// Use the composable wrapper
export function useTheme(): ThemeContext {
  const context = inject(ThemeKey);
  if (!context) throw new Error('The useTheme requires the ThemeProvider');
  return context;
}                                                                         // ✅
// Use the readonly for the state
provide(ThemeKey, { theme: readonly(theme), toggleTheme });               // ✅
// Use the Symbol for the runtime
const ThemeKey: InjectionKey<ThemeContext> = Symbol('theme');             // ✅
// Use the interface for the context
export interface ThemeContext { theme: Readonly<Ref<string>>; }           // ✅
// Export the key and the type from the library
export const ThemeKey: InjectionKey<ThemeContext> = Symbol('theme');      // ✅

❌ Don’t Do This:

// Don't use the string key in the TypeScript
const theme = inject('theme');  // the unknown                            // ❌
// Don't forget the undefined check
const theme = inject(ThemeKey);
theme.toggleTheme();  // the runtime error if undefined                    // ❌
// Don't use the wrong type
provide(ThemeKey, { theme: 'light' });  // the should be the Ref          // ❌
// Don't cast the value
const theme = inject(ThemeKey) as ThemeContext;  // the unsafe             // ❌
// Don't use the same Symbol twice
const Key1 = Symbol('theme');
const Key2 = Symbol('theme');  // the different                            // ❌
// Don't forget the readonly
provide(ThemeKey, { theme, toggleTheme });  // the mutation risk           // ❌
// Don't use the InjectionKey in the JavaScript runtime
// The InjectionKey is the TypeScript-only                                  // ✅ (the Symbol is the runtime)

Common Pitfalls

The pitfallThe whyThe fix
The unknown typeThe string keyUse the InjectionKey
The runtime errorThe no undefined checkAdd the check or the useX()
The wrong typeThe no type parameterAdd the InjectionKey<T>
The missing providerThe no useX()Use the composable
The mutationThe no readonlyWrap in the readonly
The key collisionThe stringUse the Symbol
The type mismatchThe interfaceCheck the interface

Real-World Examples

1. The Typed Key

const ThemeKey: InjectionKey<ThemeContext> = Symbol('theme');

2. The Provider

provide(ThemeKey, { theme: readonly(theme), toggleTheme });

3. The Injector

const context = inject(ThemeKey);

4. The Composable

export function useTheme(): ThemeContext { ... }

5. The Default

const context = inject(ThemeKey, defaultContext);

6. The Multiple Keys

export const AuthKey: InjectionKey<AuthContext> = Symbol('auth');

7. The app.provide

app.provide(ThemeKey, themeContext);

8. The Interface

export interface ThemeContext { theme: Readonly<Ref<string>>; }

9. The Library Export

export { ThemeKey, type ThemeContext } from './keys';

10. The Complete

const ThemeKey: InjectionKey<ThemeContext> = Symbol('theme');

Visual

The Typed Injection Flow

┌──────────────────────────────────────────────────────────────┐
│  THE TYPED INJECTION FLOW                                    │
│                                                              │
│  the-library/keys.ts:                                        │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  export interface ThemeContext {                     │    │
│  │    theme: Readonly<Ref<string>>;                     │    │
│  │    toggleTheme: () => void;                          │    │
│  │  }                                                   │    │
│  │                                                       │    │
│  │  export const ThemeKey:                              │    │
│  │    InjectionKey<ThemeContext> = Symbol('theme');     │    │
│  └──────────────────────────────────────────────────────┘    │
│    │                                                         │
│    ├── the provider imports the key                      │    │
│    │   ┌──────────────────────────────────────────────┐  │    │
│    │   │  provide(ThemeKey, themeContext);            │  │    │
│    │   │  // the value must match the ThemeContext    │  │    │
│    │   └──────────────────────────────────────────────┘  │    │
│    │                                                       │
│    └── the injector imports the key                      │    │
│        ┌──────────────────────────────────────────────┐  │    │
│        │  const context = inject(ThemeKey);           │  │    │
│        │  // the type: ThemeContext | undefined       │  │    │
│        └──────────────────────────────────────────────┘  │    │
│                                                              │
│  The compiler checks the provider and the injector.          │
│                                                              │
└──────────────────────────────────────────────────────────────┘

The Composable Wrapper

┌──────────────────────────────────────────────────────────────┐
│  THE COMPOSABLE WRAPPER                                      │
│                                                              │
│  the-library/composables.ts:                                 │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  export function useTheme(): ThemeContext {          │    │
│  │    const context = inject(ThemeKey);                 │    │
│  │    if (!context) {                                   │    │
│  │      throw new Error('The useTheme must be used      │    │
│  │        within the ThemeProvider');                   │    │
│  │    }                                                 │    │
│  │    return context;                                   │    │
│  │  }                                                   │    │
│  └──────────────────────────────────────────────────────┘    │
│    │                                                         │
│    │  the consumer                                         │
│    ▼                                                         │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  const { theme, toggleTheme } = useTheme();          │    │
│  │                                                       │    │
│  │  The type is ThemeContext, the not undefined.        │    │
│  │  The runtime check is the built-in.                  │    │
│  └──────────────────────────────────────────────────────┘    │
│                                                              │
└──────────────────────────────────────────────────────────────┘

The Interface and the Key

┌──────────────────────────────────────────────────────────────┐
│  THE INTERFACE AND THE KEY                                   │
│                                                              │
│  The interface:                                              │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  export interface ThemeContext {                     │    │
│  │    theme: Readonly<Ref<string>>;                     │    │
│  │    toggleTheme: () => void;                          │    │
│  │  }                                                   │    │
│  └──────────────────────────────────────────────────────┘    │
│                                                              │
│  The key:                                                    │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  export const ThemeKey:                              │    │
│  │    InjectionKey<ThemeContext> = Symbol('theme');     │    │
│  └──────────────────────────────────────────────────────┘    │
│                                                              │
│  The key carries the type. The provide and the inject        │
│  are the type-checked against the interface.                 │
│                                                              │
└──────────────────────────────────────────────────────────────┘

The Type Inference

┌──────────────────────────────────────────────────────────────┐
│  THE TYPE INFERENCE                                          │
│                                                              │
│  const context = inject(ThemeKey);                           │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  The type: ThemeContext | undefined                  │    │
│  │                                                       │    │
│  │  if (!context) throw new Error(...);                 │    │
│  │                                                       │    │
│  │  The type: ThemeContext                              │    │
│  └──────────────────────────────────────────────────────┘    │
│                                                              │
│  const context = inject(ThemeKey, defaultContext);           │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  The type: ThemeContext (the not undefined)          │    │
│  └──────────────────────────────────────────────────────┘    │
│                                                              │
│  The InjectionKey infers the type from the key.              │
│                                                              │
└──────────────────────────────────────────────────────────────┘

Summary

ItemValue
InjectionKey<T>The typed symbol
The declarationconst Key: InjectionKey<T> = Symbol('key')
The provideprovide(Key, value)
The injectinject(Key) returns the T | undefined
The defaultinject(Key, default) returns the T
The composableThe useX() wrapper
The interfaceThe exported type
The libraryThe exported key and type
The SymbolThe runtime
The typeThe compile-time

Key takeaways:

  • The InjectionKey<T> is the typed symbol that carries the value type. The const Key: InjectionKey<T> = Symbol('key') declares the key and the type, and the provide(Key, value) and the inject(Key) are the type-checked.
  • The inject(Key) returns the T | undefined. The undefined is the possible when no provider exists, and the check narrows the type. The inject(Key, default) returns the T without the undefined.
  • The composable wrapper handles the undefined. The useX() wraps the inject() and throws when the key is missing. The consumer uses the useX() and the return type is the typed.
  • The library exports the key and the type. The consumer imports the InjectionKey and the interface, and the provide and the inject are the type-checked against the contract.
  • The Symbol is the runtime, and the type is the compile-time. The InjectionKey extends the Symbol, and the type parameter is the TypeScript-only. The runtime is the unique symbol.
  • The readonly is the safe state. The provider wraps the state in the readonly and passes the update function. The consumer cannot mutate the state directly.
  • The type mismatch is the compile error. The provide(Key, wrongValue) is the compile error, and the inject(Key) returns the typed value. The compiler catches the mismatch.
  • The result is the type-safe dependency injection. The provider and the injector agree on the type, and the compiler enforces the agreement. The result is the foundation of the type-safe library API.

Remember: The InjectionKey<T> makes the provide and the inject the type-safe. The key carries the type, and the compiler checks the provider and the injector. The useX() composable handles the undefined, and the library exports the key and the type. The result is the type-safe dependency injection, which is the foundation of the type-safe Vue library.



Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!