CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vue--runtime-core

Vue.js 3 runtime core library providing foundational APIs for building custom renderers and managing reactive component systems

Pending
Overview
Eval results
Files

dependency-injection.mddocs/

Dependency Injection

Vue's dependency injection system enables sharing data across component hierarchies without prop drilling. It provides type-safe injection with optional default values and context checking.

Capabilities

Provide & Inject Functions

Share data from parent components to descendant components at any depth.

/**
 * Provides a value that can be injected by descendant components
 * @param key - Injection key (string or symbol)
 * @param value - Value to provide
 */
function provide<T>(key: InjectionKey<T> | string, value: T): void;

/**
 * Injects a value provided by an ancestor component
 * @param key - Injection key to look for
 * @returns Injected value or undefined if not found
 */
function inject<T>(key: InjectionKey<T> | string): T | undefined;

/**
 * Injects a value with a default fallback
 * @param key - Injection key to look for
 * @param defaultValue - Default value if injection not found
 * @returns Injected value or default value
 */
function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T;

/**
 * Injects a value with a factory function for default value
 * @param key - Injection key to look for
 * @param defaultValue - Factory function or default value
 * @param treatAsFactory - Whether to treat defaultValue as factory
 * @returns Injected value or result of default factory
 */
function inject<T>(
  key: InjectionKey<T> | string,
  defaultValue: T | (() => T),
  treatAsFactory: boolean
): T;

/**
 * Checks if injection context is available (component is in setup)
 * @returns True if injection context is available
 */
function hasInjectionContext(): boolean;

Usage Examples:

import { defineComponent, ref, provide, inject, InjectionKey } from "@vue/runtime-core";

// Define typed injection keys
const ThemeKey: InjectionKey<{ theme: string; toggleTheme: () => void }> = Symbol('theme');
const UserKey: InjectionKey<{ name: string; id: number }> = Symbol('user');

// Parent component providing values
const ParentComponent = defineComponent({
  setup() {
    const theme = ref('light');
    
    const toggleTheme = () => {
      theme.value = theme.value === 'light' ? 'dark' : 'light';
    };
    
    const user = { name: 'Alice', id: 123 };
    
    // Provide values with typed keys
    provide(ThemeKey, {
      theme: theme.value,
      toggleTheme
    });
    
    provide(UserKey, user);
    
    // Provide with string key
    provide('api-base-url', 'https://api.example.com');
    
    return { theme };
  }
});

// Child component injecting values
const ChildComponent = defineComponent({
  setup() {
    // Inject with typed key
    const themeContext = inject(ThemeKey);
    if (themeContext) {
      console.log('Current theme:', themeContext.theme);
    }
    
    // Inject with default value
    const user = inject(UserKey, { name: 'Guest', id: 0 });
    console.log('User:', user.name);
    
    // Inject string key with default
    const apiUrl = inject('api-base-url', 'https://fallback.com');
    
    // Inject with factory default
    const config = inject('app-config', () => ({ debug: false }), true);
    
    return {
      themeContext,
      user,
      apiUrl,
      config
    };
  }
});

Injection Context Checking

Verify if injection context is available before attempting injection.

/**
 * Checks if injection context is available
 * @returns True if currently in component setup context
 */
function hasInjectionContext(): boolean;

Usage Examples:

import { inject, hasInjectionContext } from "@vue/runtime-core";

// Utility function that can be called from setup or outside
function useTheme() {
  if (hasInjectionContext()) {
    // We're in a component setup context
    const theme = inject('theme', 'light');
    return { theme };
  } else {
    // We're outside component context, return default
    console.warn('useTheme called outside component context');
    return { theme: 'light' };
  }
}

const MyComponent = defineComponent({
  setup() {
    // This will work - we're in setup context
    const { theme } = useTheme();
    
    return { theme };
  }
});

// This will return default - not in component context
const { theme } = useTheme();

Advanced Injection Patterns

Common patterns for complex injection scenarios.

import { defineComponent, provide, inject, ref, computed, InjectionKey } from "@vue/runtime-core";

// Store-like pattern
interface Store {
  state: { count: number };
  getters: { doubledCount: number };
  actions: { increment(): void; decrement(): void };
}

const StoreKey: InjectionKey<Store> = Symbol('store');

const StoreProvider = defineComponent({
  setup(_, { slots }) {
    const count = ref(0);
    
    const store: Store = {
      state: { count: count.value },
      getters: {
        get doubledCount() { return count.value * 2; }
      },
      actions: {
        increment: () => count.value++,
        decrement: () => count.value--
      }
    };
    
    provide(StoreKey, store);
    
    return () => slots.default?.();
  }
});

// Configuration provider pattern
interface AppConfig {
  apiUrl: string;
  features: { darkMode: boolean; analytics: boolean };
  version: string;
}

const ConfigKey: InjectionKey<AppConfig> = Symbol('config');

const ConfigProvider = defineComponent({
  props: {
    config: { type: Object as PropType<AppConfig>, required: true }
  },
  setup(props, { slots }) {
    provide(ConfigKey, props.config);
    return () => slots.default?.();
  }
});

// Hook pattern for consuming injected values
function useStore() {
  const store = inject(StoreKey);
  if (!store) {
    throw new Error('useStore must be used within StoreProvider');
  }
  return store;
}

function useConfig() {
  const config = inject(ConfigKey);
  if (!config) {
    throw new Error('useConfig must be used within ConfigProvider');
  }
  return config;
}

Types

interface InjectionKey<T> extends Symbol {
  readonly [InjectionKeySymbol]: T;
}

type InjectionConstraint<T> = InjectionKey<T> | string;

// Helper type for creating injection keys
function createInjectionKey<T>(description?: string): InjectionKey<T>;

Best Practices

Type Safety

Use InjectionKey<T> for type-safe injection:

// Good: Type-safe injection key
const UserKey: InjectionKey<User> = Symbol('user');
provide(UserKey, user);
const injectedUser = inject(UserKey); // Type is User | undefined

// Avoid: String keys lose type information
provide('user', user);
const injectedUser = inject('user'); // Type is unknown

Error Handling

Always handle cases where injection might fail:

// Good: Handle missing injection
const user = inject(UserKey);
if (!user) {
  throw new Error('User not provided');
}

// Good: Provide default value
const user = inject(UserKey, { name: 'Guest', id: 0 });

// Good: Create helper hook
function useUser() {
  const user = inject(UserKey);
  if (!user) {
    throw new Error('useUser must be used within UserProvider');
  }
  return user;
}

Scoped Providers

Create provider components for clean injection boundaries:

const ThemeProvider = defineComponent({
  setup(_, { slots }) {
    const theme = ref('light');
    provide(ThemeKey, { theme, setTheme: (t: string) => theme.value = t });
    return () => slots.default?.();
  }
});

Install with Tessl CLI

npx tessl i tessl/npm-vue--runtime-core

docs

asset-resolution.md

builtin-components.md

components.md

composition-helpers.md

dependency-injection.md

error-handling.md

hydration.md

index.md

internal-render-helpers.md

lifecycle.md

reactivity.md

scheduler-timing.md

ssr-context.md

vdom-rendering.md

watch-effects.md

tile.json