1.0.2 • Published 12 months ago
ioc-class v1.0.2
Project Title
Simple dependency injection library for Typescript
Features
- Dependency injection using type hinting
- Support for singleton and transient dependencies
- Support to run on both browser and server
- No dependencies except
reflect-metadata
Installation
This library uses reflect-metadata
for dependency resolution, so you have to install it also.
npm install reflect-metadata
npm install ioc-class
Usage/Examples
Import the reflect-metadata
library on the project entry point.
// index.ts
import "reflect-metadata";
Declare some dependencies
class BookService {}
interface IShelfService {}
class ShelfService implements IShelfService {
public constructor(
private readonly bookService: BookService,
) {}
}
const ShelfServiceToken = new Token<IShelfService>("ShelfService");
class LibraryService {
public constructor(
@Inject(ShelfServiceToken) private readonly shelfService: IShelfService,
) {}
}
Initialize the container
const container: IContainer = new Container();
Register the dependencies in the container
// Order of registration doesn't matter
container.registerSingleton(LibraryService);
container.registerSingleton(ShelfServiceToken, ShelfService);
container.registerSingleton(BookService);
Resolve them anywhere
// Dependencies that are registered via token must be resolved via the same token
const shelfService: IShelfService = container.resolve(ShelfServiceToken, ShelfService);
// Dependencies that aren't registered via token must be resolved by passing themselves
const libraryService: LibraryService = container.resolve(LibraryService);
const bookService: BookService = container.resolve(BookService);
API Reference
Initialize container
import { Container, IContainer } from "ioc-class";
const container: IContainer = new Container();
Register a singleton dependency
class UserService {}
container.registerSingleton(UserService);
container.resolve(UserService) // UserService
Register a singleton dependency using the resolution token
import { Token } from "ioc-class";
interface IUserService {}
class UserService implements IUserService {}
const UserServiceToken = new Token<IUserService>("UserService");
container.registerSingleton(UserServiceToken, UserService);
container.resolve(UserServiceToken); // UserService
Register a transient dependency
class UserService {}
container.registerTransient(UserService);
container.resolve(UserService) // UserService
Register a transient dependency using the resolution token
import { Token } from "ioc-class";
interface IUserService {}
class UserService implements IUserService {}
const UserServiceToken = new Token<IUserService>("UserService");
container.registerTransient(UserServiceToken, UserService);
container.resolve(UserService) // UserService
Inject a dependency using the resolution token
import { Inject, Token } from "ioc-class";
interface IBookService {}
class BookService implements IBookService {}
const BookServiceToken = new Token<IBookService>("BookServiceToken");
class UserService {
public constructor(
@Inject(BookServiceToken) private readonly bookService: IBookService,
) {}
}
container.registerSingleton(BookServiceToken, BookService);
container.registerSingleton(UserService);
container.resolve(UserService); // UserService
FAQ
Can it resolve type hinted dependency when used as typed imports?
No
Can I add a proxy instance on a dependency?
Yes, but there is a catch. Consider an example where the constructor initialization is trapped using proxies:
const Decorator = <T>(target: Constructable<T>): Constructable<T> => {
return new Proxy(target, {
construct(concrete: Constructable<T>, args: Array<any>) {}
})
}
class DemoService {}
@Decorator
class UserService {
public constructor(
private readonly demoService: DemoService,
) {}
}
const container = new Container();
container.registerSingleton(UserService);
container.registerSingleton(DemoService);
const userService = container.resolve(UserService);
Here the UserService
instance will be created, but demoService
will be undefined inside of userService
. That is because when we returned the proxified constructor from the Decorator
function, the metadata properties from UserService
are lost.
To work around this issue, use the helper function copyMetadata
like this:
import { copyMetadata } from "ioc-class";
const Decorator = <T>(target: Constructable<T>): Constructable<T> => {
const proxifiedTarget = new Proxy(target, {
construct(concrete: Constructable<T>, args: Array<any>) {}
});
// This will copy the metadata properties from the original class constructor to the proxified one.
copyMetadata(target, proxifiedTarget);
return proxifiedTarget;
}