cv3-inject
Simple dependency injection for ES6
Install
$ npm install cv3-inject
Usage
Define default dependencies with Inject:
import { Inject } from 'cv3-inject/Inject';
import { Star } from './Star';
import { Lights } from './Lights';
export class XmasTree extends Inject(class{}, {
lights: Lights, star:Star
}) {
blink()
{
this.star.blink();
this.lights.blink();
}
}
Static Dependencies
Keys starting with a capital letter will not be instantiated, and instead, a reference to the class will be passed in.
(Please note this example uses object literal shorthand syntax)
import { Inject } from './Inject';
import { Drink } from './Drink';
import { Snack } from './Snack';
export class VendingMachine extends Inject(class{}, {Drink, Snack}) {
getDrink()
{
return new this.Drink;
}
getSnack()
{
return new this.Snack;
}
}
Overriding Dependencies
You can inject dependencies in-place, before instantiation. This essentially creates a new subclass on the fly. Please be mindful of the parentheses placement.
const redTree = new (Inject(XmasTree, {star: RedStar} ));
The dynamically created class will still register as an instance of the base class:
console.assert(redTree instanceof XmasTree);
Hierarchical Dependencies
const sameTree = new (Inject(XmasTree, {
star: RedStar
, lights: Inject(Lights, {star: undefined})
}));
Explcitily setting an injection to undefined
will cause the injector to check the parent injector for the same key, and if not, its parent, and so on.
Top level injections will throw an error if an injection is undefined
.
In this example, every new instance of the injected XmasTree
will hold a reference to rhe same RedStar
at instance.star
and instance.lights.star
.
Note Child injections can only access parent injections that preceed it in the precedence order in the base definition. In the above example, .star
could not be modified to hold a reference to .lights
, even if a subclass injection contained a different ordering. Please keep this in mind while designing dependence hierarchies.
Cascading Dependencies
import { Inject as I } from 'cv3-inject/Inject';
// ... more imports
export class VendingMachineTest extends I(Test
, {vendingMachine: VendingMachine}
, ({vendingMachine: VendingMachine}) => ({
sodaMachine: I(VendingMachine, {Drink:Soda})
, waterMachine: I(VendingMachine, {Drink:BottleOfWater})
, chipMachine: I(VendingMachine, {Snack:BagOfChips})
, pretzelMachine: I(VendingMachine, {Snack:BagOfPretzels})
})
, ({pretzelMachine: PretzelMachine}) => ({
pretzelAndSodaMachine: I(PretzelMachine, {Drink:Soda})
})
){...}
Note that the Inject() function has been aliased to I().
In this example, the normal dependency list specifies a dependency that will resolve to an instance of VendingMachine
, available on the .vendingMachine
property of the instance. .sodaMachine
, .waterMachine
and so on will then use VendingMachine
as their base class.
This allows us to later inject different VendingMachine
subclasses (which may have different behaviors) into the VendingMachineTest
.
Multiple dependency cascades can be can be defined to accommodate any level of complextiy. Each one will contribute to, and read from the same pool of dependencies.
Please note the double bubble notation on the arrow function: ({ })=>({ })
. This allows us to 1: Destructure our static dependency list into variables, and 2: directly return an object via shorthand, i.e. without an explict return
.
License
cv3-inject © Sean Morris 2019
All code in this package is relased under the Apache 2.0 licence.