useClass
Using useClass
in Providers
The useClass
option in a provider allows you to register a class as a dependency. When resolving the dependency, the container will instantiate and manage the class based on its lifecycle (singleton by default or transient if specified).
Example: Basic Usage
Here's how you can register a class using useClass
:
import { ContainerResolver } from "depinjionary";
import { CalculatorService } from "./calculator.service";
const providers = [
{ provide: CalculatorService, useClass: CalculatorService }
];
const container = ContainerResolver.init(providers);
const calculator = await container.resolve<CalculatorService>(CalculatorService);
console.log(calculator.calculate(5, 10)); // Outputs: 15
Binding Classes to Interfaces
One of the most powerful uses of useClass
is binding a class to an interface, allowing for better abstraction and flexibility. Here's an example:
Define an Interface
export interface LoggerInterface {
debug(data: string): void;
log(data: string): void;
}
export const LoggerInterface = Symbol('LoggerInterface');
Create a Class Implementation
export class ConsoleLoggerService implements LoggerInterface {
debug(data: string): void {
console.debug(`DEBUG: ${data}`);
}
log(data: string): void {
console.log(`LOG: ${data}`);
}
}
Register the Class to the Interface
const providers = [
{ provide: LoggerInterface, useClass: ConsoleLoggerService }
];
const container = ContainerResolver.init(providers);
// Resolving the interface token returns the implementation
const logger = await container.resolve<LoggerInterface>(LoggerInterface);
logger.log("This is a log message.");
Why Symbol Tokens?
By using Symbol
tokens for interfaces (e.g., LoggerInterface
), you ensure that the container can uniquely identify the dependency. It’s also convenient to export a Symbol
with the same name as the interface due to TypeScript’s ability to differentiate between types and values.
Benefits of useClass
- Abstraction: Decouple implementation details from the interface, making your code more flexible and testable.
- Readability: Use descriptive tokens (like the interface name) to make dependencies clear.
- Convenience: The natural ability of TypeScript to export both the interface and the symbol under the same name makes it easier to manage dependencies.
Key Notes
- Use
useClass
for cases where a class implements an interface or needs to be instantiated dynamically. - Always prefer
Symbol
orstring
tokens for interfaces to avoid naming conflicts. - Resolve dependencies using
container.resolve()
for type-safe and intuitive access.