EyeOhSee
EyeOhSee is an inversion of control (IoC) container for TypeScript.
It uses TypeScript attributes and metadata to perform constructor injection without relying on error-prone strings.
Usage
Install
To install the npm package in your project run the following command:
npm install --save eye-oh-see
Typings are included in the npm package.
Prerequisites
EyeOhSee relies on metadata that the TypeScript compiler generates. The compiler will only generate this metadata if you include the following options in your tsconfig.json
configuration file:
Basic usage
Decorate your services and implementations as shown in the features section. For example:
;
Then register service interfaces and implementations with the container. For example:
;; ;container.registerMyService;container.registerServiceImpl;
Then resolve services from the container. For example:
;
Advanced usage: Webpack automatic module/directory types registration
Decorate your services and implementations as shown in the features section.
Ensure all your services and implementations are module exports.
Install webpack-env
typings using the following command:
typings install --save --global dt~webpack-env
Register all exported services and implementations from a module/directory using require.context(...)
:
; // Module directories; // Container; // Registration of module exports in containermodules.forEach; // Resolution from container can happen from here!
Gotchas
Interfaces
TypeScript interfaces are erased and their metadata is not accessible at runtime. This means that all interfaces look the same to EyeOhSee and it is unable to resolve them correctly. We could have added support in EyeOhSee for interfaces using string identifiers to distinguish them; however, we felt this was messy, error-prone and there is an alternative.
This does not work with EyeOhSee:
This does work:
TypeScript supports multiple inheritance of fully abstract classes via the implements
keyword. This allows us to keep the same semantics but avoid using error-prone string identifiers.
Features
Implemented
- Constructor injection
- Registration of transient dependencies -
@InstancePerDependency()
- Registration of singleton dependencies -
@SingleInstance()
- Registration of implementations for abstract services -
@InstancePerDependency(BaseClass)
and@SingleInstance(BaseClass)
- Registration of a single implementation for multiple services -
@SingleInstance(BaseClassA, BaseClassB)
and@InstancePerDependency(BaseClassA, BaseClassB)
- Array injection -
@ArrayOf(BaseClass)
- Factory injection -
@Factory(ReturnType)
- Parameterized factory injection -
@Factory(ParamTypeA, ParamTypeB, ReturnType)
- Automatic disposal of resolved instances -
@Disposable()
and@Disposable(instance => instance.disposeMethod()
- Child-containers/unit-of-work injection -
@UnitOfWork(OwnedType)
- Parameterized child-container/unit-of-work factories -
@UnitOfWork(ParamTypeA, ParamTypeB, OwnedType)
- Automatic disposal of container descendants
- Ability to override attribute registration for testing using container API
- Registration of singleton-in-scope - @InstancePerScope("MyScopeName")
Code snippets
Registering a singleton
// This will only be constructed once
Registering a singleton that is resolved as its base class
// This is what consumers resolve // This is the implementation
Registering a singleton that can be resolved as multiple interfaces
// This is one of the interfaces that consumers resolve // This is another of the interfaces that consumers resolve // This is the implementation
Registering a transient
// This will be constructed for each dependency
Registering a transient that is resolved as its base class
// This is what consumers resolve // This is the implementation
Registering a transient that can be resolved as multiple interfaces
// This is one of the interfaces that consumers resolve // This is another of the interfaces that consumers resolve // This is the implementation
Array injection
// This is the service interface // This is one implementation // This is another implementation// Notice that you can mix resolution strategies of implementations // This is the consumer
Simple factory injection
// The service // The consumer
Factory injection with parameters / "robot legs"
// A foot has a toe direction // A leg has a foot // A robot has two legs
Automatic disposal
// It calls the disposal method when the container it was resolved from is disposed// It defaults to calling a method called "dispose" // We can override the method it calls when the container is disposed
Child containers / unit of work
// Service used inside unit of work // Business aspect with some lifetime that is shorter than that of the application // The application that handles the lifetimes of those business aspects
Parameterized child containers / unit of work
// Service used inside unit of work // Business aspect with some lifetime that is shorter than that of the application // The application that handles the lifetimes of those business aspects
Scoped instances
; // Service to be available as a singleton within a scope // First consumer of the service // Second consumer of the service // Representation of a request or unit of work // Application where each request