Lightweight dependency injection container for JavaScript/TypeScript
—
Comprehensive decorator system for marking classes as injectable, defining lifecycle behavior, configuring automatic registration, and specifying parameter injection patterns.
Decorators applied to class declarations to mark them for dependency injection and configure registration behavior.
Marks a class as injectable by storing dependency metadata for constructor parameters.
/**
* Marks a class as injectable, enabling constructor injection
* @param options - Optional configuration for token registration
* @returns Class decorator function
*/
function injectable<T>(options?: {
token?: InjectionToken<T> | InjectionToken<T>[]
}): ClassDecorator;Usage Examples:
// Basic injectable class
@injectable()
class UserService {
constructor(private logger: Logger, private db: Database) {}
}
// Injectable with token specification
@injectable({ token: "IUserService" })
class UserService implements IUserService {
constructor(private logger: Logger) {}
}
// Injectable with multiple tokens
@injectable({ token: ["IUserService", "IService"] })
class UserService implements IUserService, IService {
constructor(private logger: Logger) {}
}Registers a class as a singleton in the global container, ensuring only one instance exists.
/**
* Registers class as singleton in global container
* Automatically registers the class with Lifecycle.Singleton
* @returns Class decorator function
*/
function singleton<T>(): ClassDecorator;Usage Examples:
@singleton()
class ConfigurationService {
private config = { apiUrl: "https://api.example.com" };
getConfig() {
return this.config;
}
}
// Singleton with injectable
@singleton()
@injectable()
class DatabaseService {
constructor(private config: ConfigurationService) {}
}Registers a class with specific scoped lifecycle (ContainerScoped or ResolutionScoped).
/**
* Registers class with specific scoped lifecycle
* @param lifecycle - Must be Lifecycle.ContainerScoped or Lifecycle.ResolutionScoped
* @param token - Optional injection token for registration
* @returns Class decorator function
*/
function scoped<T>(
lifecycle: Lifecycle.ContainerScoped | Lifecycle.ResolutionScoped,
token?: InjectionToken<T>
): ClassDecorator;Usage Examples:
// Container scoped - one instance per container
@scoped(Lifecycle.ContainerScoped)
@injectable()
class RequestContext {
constructor(private requestId: string) {}
}
// Resolution scoped - one instance per resolution chain
@scoped(Lifecycle.ResolutionScoped, "ICache")
@injectable()
class CacheService implements ICache {
private cache = new Map();
}Creates a parameterless constructor that automatically resolves dependencies, enabling use without manual container.resolve().
/**
* Creates parameterless constructor with auto-resolved dependencies
* Enables instantiation without explicit container.resolve() call
* @returns Class decorator function
*/
function autoInjectable(): ClassDecorator;Usage Examples:
@autoInjectable()
@injectable()
class UserController {
constructor(private userService?: UserService) {}
getUsers() {
return this.userService?.getAllUsers() || [];
}
}
// Can be instantiated directly
const controller = new UserController(); // Dependencies auto-resolved
const users = controller.getUsers();Registers multiple dependencies on a class, useful for configuring related services together.
/**
* Registers multiple dependencies on class decoration
* @param registrations - Array of registration configurations
* @returns Class decorator function
*/
function registry(
registrations: ({
token: InjectionToken<any>;
options?: RegistrationOptions;
} & Provider<any>)[]
): ClassDecorator;Usage Examples:
@registry([
{ token: "ILogger", useClass: ConsoleLogger },
{ token: "IDatabase", useClass: SqlDatabase, options: { lifecycle: Lifecycle.Singleton } },
{ token: "ApiUrl", useValue: "https://api.example.com" }
])
@injectable()
class ServiceModule {
constructor() {
console.log("Service module initialized with dependencies");
}
}Decorators applied to constructor parameters to specify injection behavior and dependency resolution.
Injects a single dependency for a constructor parameter using the specified token.
/**
* Injects single dependency for constructor parameter
* @param token - Injection token to resolve
* @param options - Optional injection configuration
* @returns Parameter decorator function
*/
function inject(
token: InjectionToken<any>,
options?: { isOptional?: boolean }
): ParameterDecorator;Usage Examples:
@injectable()
class UserService {
constructor(
@inject("ILogger") private logger: ILogger,
@inject("DatabaseConfig") private config: DatabaseConfig,
@inject("Cache", { isOptional: true }) private cache?: ICache
) {}
}
// With symbols for type safety
const LOGGER_TOKEN = Symbol("ILogger");
@injectable()
class OrderService {
constructor(
@inject(LOGGER_TOKEN) private logger: ILogger
) {}
}Injects all registered dependencies for a token as an array, useful for plugin patterns.
/**
* Injects all dependencies for token as array
* Perfect for plugin patterns and multiple providers
* @param token - Injection token to resolve all instances for
* @param options - Optional injection configuration
* @returns Parameter decorator function
*/
function injectAll(
token: InjectionToken<any>,
options?: { isOptional?: boolean }
): ParameterDecorator;Usage Examples:
@injectable()
class PluginManager {
constructor(
@injectAll("Plugin") private plugins: IPlugin[],
@injectAll("Validator", { isOptional: true }) private validators: IValidator[] = []
) {}
executePlugins() {
this.plugins.forEach(plugin => plugin.execute());
}
}
// Register multiple plugins
container.register("Plugin", EmailPlugin);
container.register("Plugin", SmsPlugin);
container.register("Plugin", PushPlugin);Injects a dependency with transformation applied using a registered transformer function.
/**
* Injects dependency with transformation applied
* @param token - Injection token to resolve
* @param transformer - Injection token for transform function
* @param args - Additional arguments passed to transformer
* @returns Parameter decorator function
*/
function injectWithTransform(
token: InjectionToken<any>,
transformer: InjectionToken<Transform<any, any>>,
...args: any[]
): ParameterDecorator;Usage Examples:
// Register transformer
container.register("StringToNumber", {
useValue: { transform: (value: string) => parseInt(value, 10) }
});
@injectable()
class MathService {
constructor(
@injectWithTransform("ConfigValue", "StringToNumber")
private maxItems: number
) {}
}Injects all dependencies for a token with transformation applied to the entire array.
/**
* Injects all dependencies with transformation applied to array
* @param token - Injection token to resolve all instances for
* @param transformer - Injection token for transform function
* @param args - Additional arguments passed to transformer
* @returns Parameter decorator function
*/
function injectAllWithTransform(
token: InjectionToken<any>,
transformer: InjectionToken<Transform<[any], any>>,
...args: any[]
): ParameterDecorator;Usage Examples:
// Register array transformer
container.register("SortPlugins", {
useValue: {
transform: (plugins: IPlugin[]) => plugins.sort((a, b) => a.priority - b.priority)
}
});
@injectable()
class PluginOrchestrator {
constructor(
@injectAllWithTransform("Plugin", "SortPlugins")
private sortedPlugins: IPlugin[]
) {}
executeInOrder() {
this.sortedPlugins.forEach(plugin => plugin.execute());
}
}// Decorator function types
type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
type ParameterDecorator = (target: any, propertyKey: string | symbol, parameterIndex: number) => void;
// Injectable options
interface InjectableOptions<T> {
token?: InjectionToken<T> | InjectionToken<T>[];
}
// Injection options
interface InjectOptions {
isOptional?: boolean;
}
// Registry configuration
type RegistryConfiguration = ({
token: InjectionToken<any>;
options?: RegistrationOptions;
} & Provider<any>)[];
// Transform interface
interface Transform<TIn, TOut> {
transform(incoming: TIn, ...args: any[]): TOut;
}Install with Tessl CLI
npx tessl i tessl/npm-tsyringe