Core framework for building cloud and desktop IDE applications using modern web technologies with TypeScript and dependency injection.
—
Theia Core provides comprehensive dependency injection infrastructure built on InversifyJS, featuring Symbol-based service tokens, contribution patterns, and container management for building extensible applications.
Marks a class as injectable for dependency injection container binding.
/**
* Marks a class as available for injection
* @param target - The class to mark as injectable
* @returns The injectable class
*/
function injectable<T>(target: interfaces.Newable<T>): interfaces.Newable<T>;Usage Example:
import { injectable } from "@theia/core";
@injectable()
export class MyService {
doSomething(): void {
console.log("Service method called");
}
}Injects dependencies into constructor parameters or properties.
/**
* Injects a service into a constructor parameter
* @param serviceIdentifier - Symbol or string identifying the service
* @returns Parameter decorator
*/
function inject(serviceIdentifier: interfaces.ServiceIdentifier): ParameterDecorator;Usage Examples:
import { injectable, inject } from "@theia/core";
import { ILogger } from "@theia/core";
@injectable()
export class MyComponent {
constructor(
@inject(ILogger) private readonly logger: ILogger
) {}
performAction(): void {
this.logger.info("Action performed");
}
}Collects and provides access to all bound contributions of a specific type.
/**
* Provider for collecting contributions of a specific type
*/
interface ContributionProvider<T> {
/**
* Get all contributions of type T
* @returns Array of all bound contributions
*/
getContributions(): T[];
/**
* Get all contributions of type T that satisfy the filter
* @param filter - Optional filter function
* @returns Filtered array of contributions
*/
getContributions(filter?: (contrib: T) => boolean): T[];
}
/**
* Symbol for binding ContributionProvider
*/
const ContributionProvider: symbol;Usage Example:
import { injectable, inject } from "@theia/core";
import { ContributionProvider, CommandContribution } from "@theia/core";
@injectable()
export class CommandManager {
constructor(
@inject(ContributionProvider) @named(CommandContribution)
private readonly contributions: ContributionProvider<CommandContribution>
) {}
initializeCommands(): void {
for (const contribution of this.contributions.getContributions()) {
contribution.registerCommands(/* registry */);
}
}
}Utility functions for binding contributions and providers to the container.
/**
* Binds a contribution provider to the container
* @param bind - Container bind function
* @param id - Service identifier for the contribution type
*/
function bindContributionProvider(
bind: interfaces.Bind,
id: interfaces.ServiceIdentifier
): void;
/**
* Binds a single contribution to the container
* @param bind - Container bind function
* @param id - Service identifier for the contribution type
* @param contribution - The contribution class to bind
*/
function bindContribution<T>(
bind: interfaces.Bind,
id: interfaces.ServiceIdentifier<T>,
contribution: interfaces.Newable<T>
): void;Usage Example:
import { ContainerModule } from "inversify";
import { bindContributionProvider, CommandContribution } from "@theia/core";
export default new ContainerModule(bind => {
// Bind the contribution provider
bindContributionProvider(bind, CommandContribution);
// Bind individual contributions
bind(CommandContribution).to(MyCommandContribution).inSingletonScope();
bind(CommandContribution).to(AnotherCommandContribution).inSingletonScope();
});Decorator for named service bindings when multiple implementations exist.
/**
* Named binding decorator for distinguishing between multiple implementations
* @param name - The name to identify this binding
* @returns Parameter decorator
*/
function named(name: string | number | symbol): ParameterDecorator;Usage Example:
import { injectable, inject, named } from "@theia/core";
interface StorageService {
get(key: string): string | undefined;
set(key: string, value: string): void;
}
@injectable()
export class ConfigurationManager {
constructor(
@inject('StorageService') @named('user')
private readonly userStorage: StorageService,
@inject('StorageService') @named('workspace')
private readonly workspaceStorage: StorageService
) {}
}Utilities for working with InversifyJS containers in Theia applications.
/**
* Application container type
*/
type ApplicationContainer = interfaces.Container;
/**
* Bindable type for flexible binding
*/
type Bindable = interfaces.Bind | interfaces.Container;Usage Example:
import { Container, ContainerModule } from "inversify";
import { bindContributionProvider } from "@theia/core";
// Create application container
const container = new Container();
// Load extension modules
const extensionModule = new ContainerModule(bind => {
bindContributionProvider(bind, CommandContribution);
bind(MyService).toSelf().inSingletonScope();
});
container.load(extensionModule);
// Get services
const myService = container.get(MyService);Handle optional dependencies that may not be bound.
/**
* Optional injection decorator
* @param serviceIdentifier - Service identifier
* @returns Parameter decorator that allows undefined injection
*/
function optional(): ParameterDecorator;Usage Example:
import { injectable, inject, optional } from "@theia/core";
@injectable()
export class OptionalServiceConsumer {
constructor(
@inject('RequiredService') private readonly required: RequiredService,
@inject('OptionalService') @optional()
private readonly optional?: OptionalService
) {}
performAction(): void {
// Always available
this.required.doSomething();
// Check if optional service is available
if (this.optional) {
this.optional.doOptionalThing();
}
}
}Most Theia services should be bound in singleton scope:
bind(MyService).toSelf().inSingletonScope();Extensions contribute implementations through the contribution pattern:
// 1. Define contribution interface
interface MyContribution {
configure(): void;
}
// 2. Bind contribution provider
bindContributionProvider(bind, MyContribution);
// 3. Extensions bind their implementations
bind(MyContribution).to(MyImplementation).inSingletonScope();
// 4. Core service consumes all contributions
@injectable()
export class MyManager {
constructor(
@inject(ContributionProvider) @named(MyContribution)
private readonly contributions: ContributionProvider<MyContribution>
) {}
initialize(): void {
for (const contrib of this.contributions.getContributions()) {
contrib.configure();
}
}
}Create services with runtime parameters:
bind('ServiceFactory').toFactory(context =>
(config: ServiceConfig) => {
const service = context.container.get(ServiceImpl);
service.configure(config);
return service;
}
);// InversifyJS types re-exported from Theia
type ServiceIdentifier<T = any> = interfaces.ServiceIdentifier<T>;
type Bind = interfaces.Bind;
type Container = interfaces.Container;
type ContainerModule = interfaces.ContainerModule;
interface Newable<T> {
new (...args: any[]): T;
}
interface Abstract<T> {
prototype: T;
}Install with Tessl CLI
npx tessl i tessl/npm-theia--core