0.0.4 • Published 2 years ago

@u224/ts-context v0.0.4

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

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.

0.0.4

2 years ago

0.0.3

2 years ago

0.0.2

2 years ago

0.0.1

2 years ago