0.0.4 • Published 2 years ago
@u224/ts-context v0.0.4
ts-context
IoC container implementation and DI facilities.
Installation
npm install @u224/ts-context
Usage
A basic key-value example.
import {Context} from '@u224/ts-context';
// Create a new context.
const ctx = new Context();
// Bind 'key' to 'value'.
ctx.bind('key').to('value');
// Lookup a value by the key.
const value = ctx.getSync<string>('key');
console.log(value); // => 'value'
Type-safe binding by the BindingKey<T>
import {BindingKey} from './binding-key';
// Create `BindingKey<T>` of a number.
const key = new BindingKey<number>();
// The key `BindingKey<number>` requires
// number type of a binding value.
ctx.bind(key).to(10);
// TypeError: Argument of type 'string' is not
// assignable to parameter of type 'number'.
ctx.bind(key).to('invalid');
// Type of the bound value will be inferred
// automatically by the given key.
const value = ctx.getSync(key); // a number
console.log(value); // => 10
Class binding as singleton.
class MyService {/* ... */}
// Bind `MyService` class as a key.
ctx.bind(MyService).toClass(MyService);
// The class will be instantiated on demaind.
const service = ctx.getSync(MyService);
// A new instance was cached automatically.
const cache = ctx.getSync(MyService);
console.log(service === cache); // => true
A value factory.
// Create your own factory that returns
// a random number.
const factory = ctx => Math.random();
// Symbol keys are supported too.
const key = Symbol('rand');
// Use `toFactory` to bind the factory.
ctx.bind(key).toFactory(factory);
// Get a new factory value.
const value = ctx.getSync(key);
console.log(value); // => 0.5863144871839527
// The first value was cached automatically.
const cache = ctx.getSync(key);
console.log(value === cache); // => true
Dynamic binding and noCache
option.
// Use `noCache` option to get a new value.
const value = ctx.getSync(key, {noCache: true});
// Or make a new binding as dynamic
// to disable caching entirely.
ctx.bind('rand')
.toFactory(ctx => Math.random())
.makeDynamic();
const value1 = ctx.getSync('rand');
const value2 = ctx.getSync('rand');
console.log(value1 !== value2); // => true
Group bindings by a tag.
// Bind names with tag.
ctx.bind(Symbol()).withTag('names').to('Mary');
ctx.bind(Symbol()).withTag('names').to('Tommy');
ctx.bind(Symbol()).withTag('names').to('John');
// Find names by the tag.
const names = ctx.findSync<string>(tag);
console.log(names); // => ['Mary', 'Tommy', 'John']
Type-safe grouping by the BindingTag<T>
import {BindingTag} from './binding-tag';
// Create `BindingTag` of string values.
const tag = new BindingTag<string>('vehicles');
// Bind vehicles with tag.
ctx.bind(Symbol()).withTag(tag).to('bicycle');
ctx.bind(Symbol()).withTag(tag).to('wagon');
ctx.bind(Symbol()).withTag(tag).to('car');
// TypeError: Argument of type 'number' is not
// assignable to parameter of type 'string'.
ctx.bind(Symbol()).withTag(tag).to(100);
// Find vehicles by the tag.
const names = ctx.findSync(tag);
console.log(names); // => ['bicycle', 'wagon', 'car']
Optional values and error handling.
// Create a named context to improve
// errors verbosity.
const ctx = new Context('myCtx');
// NoValueBindingError: String(notExistingKey) does
// not have a value binding in the Context(myCtx).
const value1 = ctx.getSync('notExistingKey');
// Do use `optional: true` to get `undefined`
// instead of throwing error if a given key
// is not bound.
const value2 = ctx.getSync(
'notExistingKey',
{optional: true},
);
console.log(value2); // => undefined
Resolve binding asynchronously.
// Create a factory that returns a promise value.
const factory = () => Promise.resolve('world');
// Bind 'hello' to the factory.
ctx.bind('hello').toFactory(factory);
// SyncResolutionError: Cannot get 'hello'
// synchronously from the Context.
const value1 = ctx.getSync<string>('hello');
// Do use `get` instead of `getSync` to resolve
// value asynchronously.
const value2 = await ctx.get<string>('hello');
console.log(value2); // => 'world'
Dependency injection using @inject
decorator.
import {inject} from '@u224/ts-context';
class HelloController {
// Inject a value bound to `greeting`.
@inject('greeting')
greeting: string;
constructor(
// Inject a value bound to `name`.
@inject('name')
readonly name: string,
) {}
greeting() {
console.log(`${this.greeting} ${this.name}`);
}
}
// Bind 'greeting' to 'Hello'
ctx.bind('greeting').to('Hello');
// and 'name' to 'John'.
ctx.bind('name').to('John');
// Bind HelloController as a singleton.
ctx.bind(HelloController).toClass(HelloController);
// Get an instance of HelloController.
const helloController = ctx.getSync(HelloController);
// Instance of HelloController now has the `greeting`
// and `name` properties injected using context.
helloController.greeting(); // => Hello John
Locked binding prevents value overwriting.
// Create locked binding.
ctx.bind('key').to('foo').lock();
// LockedBindingError: Unable to
// modify String(key) its locked.
ctx.bind('key').to('bar');
Context keys
BindingKey<T>
Constructor<T>
string
symbol
Context tags
BindingTag<T>
Constructor<T>
string
symbol
Testing
npm run test
License
MIT
Inspired by IBM LoopBack.