interface IConfig < T > { object ? : INewAble < T > ; factory ? : Factory < T > ; value ? : Value < T > ; cache ? : T ; singleton : boolean ; } interface INewAble < T > { new ( ... args ) : T ; } type Registry = Map < symbol , IConfig < any > > ; type Factory < T > = ( ) => T ; type Value < T > = T ; class Options < T > { constructor ( private _target ) { } inSingletonScope ( ) { this . _target . singleton = true ; } } class Bind < T > { constructor ( private _target ) { } to ( object ) { this . _target . object = object ; return new Options < T > ( this . _target ) ; } toFactory ( factory ) { this . _target . factory = factory ; return new Options < T > ( this . _target ) ; } toValue ( value ) { if ( typeof value === "undefined" ) { throw "cannot bind a value of type undefined" ; } this . _target . value = value ; } } export class Container { private _registry : Registry = new Map < symbol , IConfig < any > > ( ) ; private _snapshots : Registry [ ] = [ ] ; bind < T = never > ( type ) { return new Bind < T > ( this . _add < T > ( type ) ) ; } rebind < T = never > ( type ) { return this . remove ( type ) . bind < T > ( type ) ; } remove ( type ) { if ( this . _registry . get ( type ) === undefined ) { throw `${ type . toString ( ) } was never bound` ; } this . _registry . delete ( type ) ; return this ; } get < T = never > ( type ) { const regItem = this . _registry . get ( type ) ; if ( regItem === undefined ) { throw `nothing bound to ${ type . toString ( ) }` ; } const { object , factory , value , cache , singleton } = regItem ; const cacheItem = ( creator ) : T => { if ( singleton && typeof cache !== "undefined" ) return cache ; if ( ! singleton ) return creator ( ) ; regItem . cache = creator ( ) ; return regItem . cache ; } ; if ( typeof value !== "undefined" ) return value ; if ( typeof object !== "undefined" ) return cacheItem ( ( ) => new object ( ) ) ; if ( typeof factory !== "undefined" ) return cacheItem ( ( ) => factory ( ) ) ; throw `nothing is bound to ${ type . toString ( ) }` ; } snapshot ( ) { this . _snapshots . push ( new Map ( this . _registry ) ) ; return this ; } restore ( ) { this . _registry = this . _snapshots . pop ( ) || this . _registry ; return this ; } private _add < T > ( type ) { if ( this . _registry . get ( type ) !== undefined ) { throw `object can only bound once: ${ type . toString ( ) }` ; } const conf = { singleton : false } ; this . _registry . set ( type , conf ) ; return conf ; } }