Small, simple, type-safe DI library.
Easy to use DI library.
- Size < 1KB
- Only 3 core functions to learn
- Minimal boilerplate
- Fully type-safe
- No string keys
- No decorators
- Localized and controlled mutations
Demo
InversifyJS example
API
Install
npm install @dhmk/di
Quick demo
import { transient, singleton, withContainer, createContainer } from "@dhmk/di";
const d1 = transient(Math.random);
const d2 = singleton(Math.random);
d1();
const d3 = singleton((a = d1(), b = d2()) => {
return a + b;
});
withContainer(() => {
d1();
d1();
d2();
d2();
return d3();
});
withContainer(d2);
const cont = createContainer();
withContainer(d2, cont) === withContainer(d2, cont);
const cont2 = createContainer(cont);
withContainer(d2, cont) === withContainer(d2, cont2);
withContainer((bind) => {
bind(d1, () => 123);
bind(
d2,
singleton(() => 123)
);
});
InversifyJS example
Here is an adapted example from InversifyJS.
import { singleton, factory, withContainer } from "@dhmk/di";
interface Warrior {
fight(): string;
sneak(): string;
}
interface Weapon {
hit(): string;
}
interface ThrowableWeapon {
throw(): string;
}
const getKatana = singleton<Weapon>(() => ({
hit() {
return "cut!";
},
}));
class Shuriken {
public throw() {
return "hit!";
}
}
const getShuriken = singleton<ThrowableWeapon>(() => new Shuriken());
const getNinja = singleton<Warrior>(
factory(
class Ninja {
private _katana: Weapon;
private _shuriken: ThrowableWeapon;
public constructor(katana = getKatana(), shuriken = getShuriken()) {
this._katana = katana;
this._shuriken = shuriken;
}
public fight() {
return this._katana.hit();
}
public sneak() {
return this._shuriken.throw();
}
}
)
);
const ninja = withContainer(getNinja);
console.log(ninja.fight());
console.log(ninja.sneak());
const ninja2 = withContainer((bind) => {
bind(getKatana, () => ({ hit: () => "shoot!" }));
return getNinja();
});
console.log(ninja2.fight());
console.log(ninja2.sneak());
API
type Dependency<T> = () => T
A dependency is just a function that returns a value.
transient(fn): Dependency
Creates transient dependency. fn
will be called each time dependency is read.
singleton(fn): Dependency
Creates singleton dependency. fn
will be called once per container and result will be cached.
withContainer(fn, container?): ReturnType<fn>
fn: (bind) => T
bind: (dep, newFn) => void
Runs fn
inside container
(or temporary container) and returns result.
fn
is called with bind
function, which can be used to replace a function of a given dependency for this container.
withContainer((bind) => {
bind(someDependency, newFunction);
bind(someDependency, singleton(newFunction));
bind(someDependency, singletonFunction(newFunction));
bind(someDependency, otherDependency);
});
createContainer(otherContainer?): Container
Creates container for storing dependencies state. If otherContainer
is provided, also copies its state.
Other API
dependency(fn): Dependency
Core dependency creator. transient
is an alias to this function.
factory(Class): Function
A shortcut to this:
class SomeClass {}
transient(() => new SomeClass());
transient(factory(class SomeClass {}));
singletonFunction(fn): fn
Creates singleton function. It will be called once per container.
singleton
dependency is build around singletonFunction
:
const singleton = (fn) => dependency(singletonFunction(fn));
getCaller(): Dependency | undefined
Returns a dependency (if any) from which current dependency has been called. Can be used to return different values for different parent dependecies:
const d1 = transient(() => dep());
const d2 = transient(() => dep());
const dep = transient(() => {
switch (getCaller()) {
case d1:
return 1;
case d2:
return 2;
}
});
Types
MutableContainer<Allow = All, Deny = None>
Only types which extend Allow
and don't extend Deny
can be mutated with bind
inside withContainer
.
createContainer() as MutableContainer<string | number, boolean | Date>;
ReadonlyContainer
Alias for MutableContainer<None>
All
None