Giter Site home page Giter Site logo

thiagobustamante / typescript-ioc Goto Github PK

View Code? Open in Web Editor NEW
521.0 13.0 63.0 873 KB

A Lightweight annotation-based dependency injection container for typescript.

License: MIT License

TypeScript 99.43% JavaScript 0.57%
ioc-container typescript ioc cdi dependency-injection decorators es7-decorators

typescript-ioc's Introduction

npm version Build Status Coverage Status Known Vulnerabilities

IoC Container for Typescript - 3.X

This is a lightweight annotation-based dependency injection container for typescript.

It can be used on browser, on react native or on node.js server code.

The documentation for the previous version can be found here

Table of Contents

Installation

This library only works with typescript. Ensure it is installed:

npm install typescript -g

To install typescript-ioc:

npm install typescript-ioc

Configuration

Typescript-ioc requires the following TypeScript compilation options in your tsconfig.json file:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "target": "es6" // or anything newer like esnext
  }
}

Basic Usage

import {Inject} from "typescript-ioc";

class PersonDAO {
  @Inject
  restProxy: PersonRestProxy;
}

That's it. You can just call now:

let personDAO: PersonDAO = new PersonDAO();

And the dependencies will be resolved.

You can also inject constructor parameters, like:

class PersonService {
  private personDAO: PersonDAO;
  constructor( @Inject personDAO: PersonDAO ) {
    this.personDAO = personDAO;
  }
}

and then, if you make an injection to this class, like...

class PersonController {
  @Inject
  private personService: PersonService;
}

The container will create an instance of PersonService that receives the PersonDAO from the container on its constructor. But you can still call:

let personService: PersonService = new PersonService(myPersonDAO);

And pass your own instance of PersonDAO to PersonService.

Note that any type with a constructor can be injected.

class PersonController {
  @Inject
  private personService: PersonService;

  @Inject
  creationTime: Date;
}

Inheritance

You don't have to do anything special to work with sub-types.

abstract class BaseDAO {
  @Inject
  creationTime: Date;
}

class PersonDAO extends BaseDAO {
  @Inject
  private personRestProxy: PersonRestProxy;
}

class ProgrammerDAO extends PersonDAO {
  @Inject
  private programmerRestProxy: PersonRestProxy;
}

The above example will work as expected.

Scopes

You can use scopes to manage your instances. We have three pre defined scopes (Scope.Singleton, Scope.Request and Scope.Local), but you can define your own custom Scope.

@Singleton

Allow just one instance for each type bound to this scope.

@Singleton 
class PersonService {
  @Inject
  private personDAO: PersonDAO;
}

class PersonController {
  @Inject
  private personService: PersonService;

  @Inject
  creationTime: Date;
}

So, we can create a lot of PersonController instances, but all of them will share the same singleton instance of PersonService

let controller1: PersonController = new PersonController();
let controller2: PersonController = new PersonController();

@InRequestScope

Types bound to this scope will share instances between the same build context. When you call Container.get, a new build context is created and every container resolution performed will share this context.

For example:

@InRequestScope
class RequestScopeClass {}

class FirstClass {
    @Inject
    public a: RequestScopeClass;
}

class SecondClass {
    @Inject
    public a: RequestScopeClass;
    @Inject
    public b: FirstClass;
}

In that example, we can expect:

const secondClass = Container.get(SecondClass);
expect(secondClass.a).toEqual(secondClass.b.a);

Local Scope

The container will create a new instance every time it will be asked to retrieve objects for types bound to the Local scope.

The Local scope is the default scope. So you don't need to configure nothing to work with a Local scope. However if you have a Type bound to other scope and want to change it to the Local scope, you can use the Scope.Local property:

@Singleton
class MyType {}

Container.bind(MyType).scope(Scope.Local);

Custom Scopes

To define a new scope, you just have to extend the Scope abstract class:

class MyScope extends Scope { 
  resolve(factory: ObjectFactory, source:Function, context: BuildContext) {
    console.log('created by my custom scope.')
    return factory(context);
  }
}
@Scoped(new MyScope()) 
class PersonService {
  @Inject
  private personDAO: PersonDAO;
}

Factories

Factories can be used to create the instances inside the IoC Container.

const personFactory: ObjectFactory = () => new PersonService(); 
@Factory(personFactory)
class PersonService {
  @Inject
  private personDAO: PersonDAO;
}

The Factory method will receive the BuildContext as parameter. So, if you need to retrieve another instance from the container to perform the factory instantiation, you can ask it to the BuildContext. For example:

const personFactory: ObjectFactory = (context) => new PersonService(context.resolve(PersonDAO)); 
@Factory(personFactory)
class PersonService {
  constructor(private personDAO: PersonDAO){}
}

The @OnlyInstantiableByContainer annotation

The @OnlyInstantiableByContainer annotation transforms the annotated class, changing its constructor. So, it will only be able to create new instances for the decorated class through to the IoC Container.

It is usefull, for example, to avoid direct instantiation of Singletons.

@Singleton 
@OnlyInstantiableByContainer 
class PersonService {
  @Inject
  private personDAO: PersonDAO;
}

If anybody try to invoke:

new PersonService();

Will prodeuce a TypeError.

The Container class

You can also bind types directly to Container resolution.

// it will override any annotation configuration
Container.bind(PersonDAO).to(ProgrammerDAO).scope(Scope.Local); 

// that will make any injection to Date to return 
// the same instance, created when the first call is executed.
Container.bind(Date).to(Date).scope(Scope.Singleton); 

// it will ask the IoC Container to retrieve the instance.
let personDAO = Container.get(PersonDAO); 
class PersonDAO {
  @Inject
  private personRestProxy: PersonRestProxy;
}

Container.bind(PersonDAO); 
let personDAO: PersonDAO = Container.get(PersonDAO); 
// or
let otherPersonDAO: PersonDAO = new PersonDAO(); 
// personDAO.personRestProxy is defined. It was resolved by Container.
@OnlyInstantiableByContainer
@Singleton
class PersonDAO {
}

let p: PersonDAO = new PersonDAO(); // throws a TypeError.  classes decorated with @OnlyInstantiableByContainer can not be instantiated directly

const personFactory: ObjectFactory = () => new PersonDAO();
Container.bind(PersonDAO).factory(personFactory); //Works OK

let personDAO = Container.get(PersonDAO); // Works OK

@InjectValue decorator and Constants

It is possible to bind constants to the Container. It is useful for configurations, for example.

interface Config {
    dependencyURL: string;
    port: number;
}

Container.bindName('config').to({
    dependencyURL: 'http://localhost:8080',
    port: 1234
});

And then you can use the @InjectValue decorator exactly as you use @Inject to inject instances.

class MyService {
    constructor(@InjectValue('config') public config: Config) { }
}

It is possible to inject an internal property from a constant, like:

class MyService {
    constructor(@InjectValue('config.dependencyURL') private url: string,
                @InjectValue('myConfig.otherProperty.item[0].otherURL') private otherURL: string) { }
}

And also to mix constants and other container injections, like:

class MyService {
    constructor(@InjectValue('config.dependencyURL') private url: string,
                @InjectValue('myConfig.otherProperty.item[0].otherURL') private otherURL: string, 
                @Inject private myRepository: MyRepository) { }
}

Value Injections can be used direclty in class properties:

class MyService {
    @InjectValue('config.dependencyURL') 
    private url: string;
    
    @InjectValue('myConfig.otherProperty.item[0].otherURL') 
    private otherURL: string;
    
    @Inject 
    private myRepository: MyRepository;
}

Or read directly from the Container:

const url: string = Container.getValue('config.dependencyURL');

It is possible to bind an internal property of a constant, like:

Container.bindName('config.dependencyURL').to('http://anewURL.com');

Namespaces (Environments)

It is possible to create specific namespaces with custom configurations and then tell container to use these namespaces.

For example:

Container.bindName('config.dependencyURL').to('http://myURL.com');
const namespace = Container.namespace('test');
Container.bindName('config.dependencyURL').to('http://anewURL.com');

Only if the namespace 'test' is active, the 'config.dependencyURL' will resolve to 'http://anewURL.com'.

To use the default namespace, just call Container.namespace(null).

If you want to remove a namespace, just call namespace.remove()

const namespace = Container.namespace('test');
namespace.remove();

It is not possible to remove the default namespace.

An alias called 'environment' is defined for the namespace method:

Container.namespace('test');
Container.environment('test'); // both commands are equivalents

Take a look at here for more examples of namespaces usage.

Creating temporary configurations

You can use snapshot for testing or where you need to temporarily override a binding.

describe('Test Service with Mocks', () => {

    const snapshot: Snapshot;
    before(function () {
        // Store the IoC configuration
        snapshot = Container.snapshot();
        
        // Change the IoC configuration to a mock service.
        Container.bind(IService).to(MockService);
    });

    after(function () {
        // Put the IoC configuration back for IService, so other tests can run.
        snapshot.restore();
    });

    it('Should do a test', () => {
        // Do some test
    });
});

Importing configurations from external file

You can put all manual container configurations in an external file and then use the '''Container.configure''' method to import them.

For example, you can create the ioc.config.ts file:

import { MyType, MyTypeImpl, MyType2, MyType2Factory } from './my-types';
import { Scope } from 'typescript-ioc';
import * as yaml from 'js-yaml';
import * as fs from 'fs';

const config = yaml.safeLoad(fs.readFileSync('service-config.yml', 'utf8'));

export default [
  { bind: MyType, to: MyTypeImpl },
  { 
    bind: MyType2, 
    factory: MyType2Factory, 
    withParams: [Date], 
    scope: Scope.Singleton 
  },
  { bindName: 'config', to: config }
];

And then import the configurations using:

import { Container } from "typescript-ioc";
import config from './ioc.config';

Container.configure(config);

You need to load the configurations only once, but before you try to use the objects that depends on these files.

You can create configurations for specific namespaces, like:

import { MyRepository, MyTestRepository } from './my-types';
import * as yaml from 'js-yaml';
import * as fs from 'fs';

const config = yaml.safeLoad(fs.readFileSync('service.config.yml', 'utf8'));
const configTest = yaml.safeLoad(fs.readFileSync('service.config-test.yml', 'utf8'));
const configProd = yaml.safeLoad(fs.readFileSync('service.config-prod.yml', 'utf8'));

export default [
  { bindName: 'config', to: config },
  { namespace: {
      test: [
        { bindName: 'config', to: configTest },
        { bind: MyRepository, to: MyTestRepository },
      ],
      production: [
        { bindName: 'config', to: configProd }
      ]
    }
  }
];

A note about classes and interfaces

Typescript interfaces only exist at development time, to ensure type checking. When compiled, they do not generate runtime code. This ensures good performance, but also means that is not possible to use interfaces as the type of a property being injected. There is no runtime information that could allow any reflection on interface type. Take a look at microsoft/TypeScript#3628 for more information about this.

So, this is not supported:

interface PersonDAO {
  get(id: string): Person;
}

class ProgrammerDAO implements PersonDAO {
  @Inject
  private programmerRestProxy: PersonRestProxy;

  get(id: string): Person
  {
      // get the person and return it...
  }
}

Container.bind(PersonDAO).to(ProgrammerDAO); // NOT SUPPORTED

class PersonService {
  @Inject // NOT SUPPORTED
  private personDAO: PersonDAO;
}

However there is no reason for panic. Typescript classes are much more than classes. It could have the same behavior that interfaces on other languages.

So it is possible to define an abstract class and then implement it as we do with interfaces:

abstract class PersonDAO {
  abstract get(id: string): Person;
}

class ProgrammerDAO implements PersonDAO {
  @Inject
  private programmerRestProxy: PersonRestProxy;

  get(id: string): Person
  {
      // get the person and return it...
  }
}

Container.bind(PersonDAO).to(ProgrammerDAO); // It works

class PersonService {
  @Inject // It works
  private personDAO: PersonDAO;
}

The abstract class in this example has exactly the same semantic that the typescript interface on the previous example. The only difference is that it generates type information into the runtime code, making possible to implement some reflection on it.

Examples

Using Container for testing

Some examples of using the container for tests:

describe('My Test', () => {
    let myService: MyService;
    beforeAll(() => {
        class MockRepository implements AuthenticationRepository {
          async getAccessToken() {
            return 'my test token';
          }
        }
        Container.bind(AuthenticationRepository).to(MockRepository)
        myService = Container.get(MyService);
    });
    //...
});

or you can configure all your mocks togheter in a mocks.config.ts

class MockRepository implements AuthenticationRepository {
  async getAccessToken() {
    return 'my test token';
  }
}

class OtherMockRepository implements OtherRepository {
  async doSomething() {
    return 'done';
  }
}

export default [
  { bind: AuthenticationRepository, to: MockRepository },
  { bind: OtherRepository, to: OtherMockRepository }
];

and then in your test files:

import mocksConfig from './mocks.config.ts';

describe('My Test', () => {
    let myService: MyService;
    beforeAll(() => {
        Container.config(mocksConfig);
        myService = Container.get(MyService);
    });
    //...
});

or if you want to use the configurations and restore the container after the test:

import mocksConfig from './mocks.config.ts';

describe('My Test', () => {
    let myService: MyService;
    let snaphot: Snaphot;
    beforeAll(() => {
        snapshot = Container.snapshot();
        Container.config(mocksConfig);
        myService = Container.get(MyService);
    });

    afterAll(() => {
        snapshot.restore();
    });

    //...
});

Using Namespaces

Define configurations on a file, like ioc.config.ts:

import { MyRepository, MyTestRepository } from './my-types';
import * as yaml from 'js-yaml';
import * as fs from 'fs';

const config = yaml.safeLoad(fs.readFileSync('service.config.yml', 'utf8'));
const configTest = yaml.safeLoad(fs.readFileSync('service.config-test.yml', 'utf8'));
const configProd = yaml.safeLoad(fs.readFileSync('service.config-prod.yml', 'utf8'));

export default [
  { bindName: 'config', to: config },
  { env: {
      test: [
        { bindName: 'config', to: configTest },
        { bind: MyRepository, to: MyTestRepository },
      ],
      production: [
        { bindName: 'config', to: configProd }
      ]
    }
  }
];

And then import the configurations using:

import { Container } from "typescript-ioc";
import config from './ioc.config';

Container.configure(config);
// Then activate the environment calling the container 
Container.environment(process.env.NODE_ENV);

Browser usage

It was tested with browserify and webpack, but it should work with any other similar tool.

Starting from version 2, this library only works in browsers that supports javascript ES6 '''class'''. If you need to support old ES5 browsers, please use the version 1.2.6 of this library

Restrictions

  • Circular injections are not supported

Migrating from previous version

Some breaking changes:

ES6 suport

This library does not support old ES5 code anymore. So, you need to change the target compilation of your code to es6 (or anything else newer, like es2016, es2020, esnext etc)

Your tsconfig.json needs to include at least:

{
   "compilerOptions": {
       "experimentalDecorators": true,
       "emitDecoratorMetadata": true,
       "target": "es6"
   }
}

This decision was taken to help to solve a lot of bugs with react native and browser environments.

If you need to support es5 code, you can keep using the 1.2.6 version

@AutoWired renamed

A lot of confusion with @AutoWired motivated us to rename it to @OnlyInstantiableByContainer. It is a big name, but it says exactly what that decorator does. It is completely optional (The container will always work in the same way when instrumenting the types), but it transforms the decorated constructor to avoid that anybody create new instances calling direct a new expression.

So you need to change all references to @AutoWired to @OnlyInstantiableByContainer.

@Provided @Provides and Provider interface removed

We changed the name of the interface Provider to ObjectFactory and also change the definition of this type to be a simple function signature.

So, now we have:

// previous version
const provider = {
  get: () => new MyType()
};

// new version
const factory = () => new MyType();

Following the same design, whe renamed the @Provideddecorator to @Factory.

// previous version
@Provided({
  get: () => new MyType()
})
class MyType {
}

// new version
@Factory(() => new MyType())
class MyType {
}

The @Provides decorator was removed because it could cause a lot of problems, once it was used in the class that would provide an implementation, that usually was always defined in different files. That forced us to had things like ContainerConfig.addSource() to scan folders for files. It caused problems in react native, in browser and in some environments like lambda functions.

We redesigned a new way to load container configurations that does not need to scan folders anymore, removing the problems and improving the performance. Take a look at Container.configure method for a better option for the old @Provides.

Container.snapshot refactored

We had a minor change in the Snapshot handling. We don't have anymore the public method Container.restore(type). A safer way to work with snapshots was implemented. Now the Container.snapshot method returns a snapshot object. That object has a restore() method.

The new way:

const snapshot = Container.snapshot();
snapshot.restore();

typescript-ioc's People

Contributors

cfmano avatar kazzkiq avatar mnemonic101 avatar ryanstreur avatar snejati86 avatar thiagobustamante avatar thorstenbux avatar westinpigott avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

typescript-ioc's Issues

Uncaught TypeError: fs.existsSync is not a function in browser

I've got an error "Uncaught TypeError: fs.existsSync is not a function" in Chrome.

Steps to reproduce:

  1. Create app
create-react-app app1 --scripts-version=react-scripts-ts
cd app1
npm install --save typescript-ioc
  1. Add this to tsconfig.json:
{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

All other options leave as they are.

  1. Add code to src/index.tsx
import { Container } from 'typescript-ioc';
Container.bind(Date).to(Date);
  1. See a blank page and get an error: "Uncaught TypeError: fs.existsSync is not a function" in console

May be I should do anything else to make this work?

PS. For clearify I have exactly that tsconfig.json:

{
  "compilerOptions": {
    "outDir": "build/dist",
    "module": "commonjs",
    "target": "es5",
    "lib": ["es6", "dom"],
    "sourceMap": true,
    "allowJs": true,
    "jsx": "react",
    "moduleResolution": "node",
    "rootDir": "src",
    "forceConsistentCasingInFileNames": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "suppressImplicitAnyIndexErrors": true,
    "noUnusedLocals": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  },
  "exclude": [
    "node_modules",
    "build",
    "scripts",
    "acceptance-tests",
    "webpack",
    "jest",
    "src/setupTests.ts"
  ],
  "types": [
    "typePatches"
  ]
}

How to use singleton scope

So I tried following the getting started from NPM however, i stumbled across this problem: when using new ClassName() when that class has the @Singleton decorator, it still calls the constructor of that class every time I call new ClassName(), it'll only call once if I use Container.get(ClassName).

Example:

@Singleton
class Test{

    constructor() {
        new Test1(10);
        new Test1();
        new Test1();
    }
}

@Singleton
class Test1 {
    constructor() {
        console.log("Test1");
    }
}

Will print the following:

Test1
Test1
Test1

While this:

@Singleton
class Test{

    constructor() {
        Container.get(Test1);
        Container.get(Test1);
        Container.get(Test1);
    }
}

@Singleton
class Test1 {
    a: number;

    constructor(a?: number) {
        if(a) this.a = a;
        console.log("Test1");
    }
}

Would print this:

Test1

Why is that the case with the first example?

override Container.bind()

Is there a way to override the bound types? For example:

Container.bind(AuthGuard).to(MockGuard);
    ...
Container.bind(AuthGuard).to(AnotherMockGuard);

Currently I am not able to bind AnotherMockGuard; Container.get(AuthGuard) returns MockGuard

Recursive container instantiation of OnlyInstantiableByContainer classes fails

Affected version: 3.0.3
Error: TypeError: Can not instantiate it. The instantiation is blocked for this class. Ask Container for it, using Container.get

Some properties are injected without any problems. But injection fails for classes that themselves have injected properties whose types are decorated with @OnlyInstantiableByContainer. Thus, it would seem that recursive container instantiation of @OnlyInstantiableByContainer classes is the issue. This issue manifests itself in InjectionHander class. I've stepped through the code, and here's the sequence of events:

  1. "problem" class instrumented constructor is called
  2. super(...args) succeeds
    ** other @OnlyInstantiableByContainer instantiation happens during this call
  3. then InjectorHandler.assertInstantiable() fails

Full stack trace:

Function.assertInstantiable (.../node_modules/typescript-ioc/dist/container/injection-handler.js:83:19)
    at new ioc_wrapper (.../node_modules/typescript-ioc/dist/container/injection-handler.js:15:33)
    at IoCBindConfig.callConstructor (.../node_modules/typescript-ioc/dist/container/container-binding-config.js:30:65)
    at .../node_modules/typescript-ioc/dist/container/container-binding-config.js:18:29
    at iocFactory (.../node_modules/typescript-ioc/dist/container/container-binding-config.js:37:30)
    at SingletonScope.resolve (.../node_modules/typescript-ioc/dist/scopes.js:21:24)
    at IoCBindConfig.getInstance (.../node_modules/typescript-ioc/dist/container/container-binding-config.js:71:30)
    at get (.../node_modules/typescript-ioc/dist/container/container.js:26:23)
    at Main.get [as _expressController] (.../node_modules/typescript-ioc/dist/container/injection-handler.js:59:72)
    at Main.start (.../dist/index.js:25:27

Screenshot of debugging session:
image

Can not instantiate Singleton class - error when using uglifyJS mangle option

If I try to pack my project with uglifyJS and allow it to mangle variable names the IOC container breaks:
Uncaught TypeError: Can not instantiate Singleton class. Ask Container for it, using Container.get

Is there any workaround for this except not mangling variables?
If I specify "mangle: false" the IOC container works as expected.

Async provider

Hey guys,

is there any support for async providers / resolving dependencies asynchronously?

Example:

const configProvider: Provider = {
  async get () => {
    const config = await loadConfig(process.env.NODE_ENV)
    return config
  }
}

Many thanks
Juraj

CHANGELOG

could you please create and update changelog for the releases? Would be really helpfull

Webworker support

When using typescript-ioc in a webworker, the following error occurs:
index.js?40f3:11 Uncaught TypeError: fs_1.existsSync is not a function

The isBrowser() check is correct, because a webworker doesn't has a renderer or window object. But it isn't a Node.js environment either.

I manually fixed it by using the following:

'use strict';
var isBrowser = new Function('try {return this===window;}catch(e){return false;}');
var useES6 = false;
if (!isBrowser() && !(self instanceof WorkerGlobalScope)) {
useES6 = process.env.ES6 === 'true';
if (!useES6) {
var fs_1 = require('fs');
var path_1 = require('path');
var searchConfigFile = function () {
var configFile = path_1.join(__dirname, 'ioc.config');
while (!fs_1.existsSync(configFile)) {
var fileOnParent = path_1.normalize(path_1.join(path_1.dirname(configFile), '..', 'ioc.config'));
if (configFile === fileOnParent) {
return null;
}
configFile = fileOnParent;
}
return configFile;
};
var CONFIG_FILE = searchConfigFile();
if (CONFIG_FILE && fs_1.existsSync(CONFIG_FILE)) {
var config = JSON.parse(fs_1.readFileSync(CONFIG_FILE));
if (config.es6) {
useES6 = true;
}
}
}
}
module.exports = require(useES6 ? './es6' : './es5');

I can create a pull request for this, but it's just a trivial change.

Not working on React native

I'm not able to build my react-native app using this awesome module. I have this error inside metro bundler:

error: bundling failed: Error: Unable to resolve module `fs` from `/Users/ XXX /node_modules/typescript-ioc/index.js`: Module `fs` does not exist in the Haste module map

my packages:

    "tslint": "^5.14.0",
    "react": "16.8.3",
    "react-native": "0.59.8",
    "react-native-router-flux": "^4.0.6",
    "react-native-vector-icons": "^4.6.0",
    "typescript-ioc": "^1.2.5"

    "typescript": "^3.4.5"

Class constructor PersonRestProxy cannot be invoked without 'new'

Hi!
I would really like to use your library, but I ran into weird errors like this one. Can you maybe give me a hint? thx!

import { Singleton, AutoWired, Inject, Container } from 'typescript-ioc';
@Singleton
@AutoWired
class PersonRestProxy {
  public random;
  constructor () {
    this.random = Math.random();
  }
}

class PersonDAO {
  @Inject
  public creationTime: Date;

  @Inject
  public restProxy: PersonRestProxy;
}

class PersonDAO3 {
  @Inject
  public creationTime: Date;

  @Inject
  public restProxy: PersonRestProxy = new PersonRestProxy();
}

let personDAO: PersonDAO = new PersonDAO();
console.log('**1', personDAO.creationTime, personDAO.restProxy.random);

let personDAO2: PersonDAO = new PersonDAO();
console.log('**2', personDAO.creationTime,  personDAO2.restProxy.random);

let personDAO3: PersonDAO3 = new PersonDAO3();
console.log('**3', personDAO3.creationTime,  personDAO3.restProxy.random);

Set constants

Is there a way to set constants to container?

For example
const config:IConfig = {
connectionString: "xpto"
};

Container.bind(config).provider(config).

Handling connection errors

Hi, Basically we are using the Hyperledger client to build our application and we are working on a rest api interfacing with the chain.

I have a class called HyperledgerChainClient which inherits BusinessNetworkConnection (a class provided by hyperledger) i need it to be instantiated and connected when my server starts, so my connection will always be the same.
my class looks like this

@Singleton
export class HyperledgerChainClient extends BusinessNetworkConnection {
  constructor () {
    super(config.get('hyperledger.cardName'));
  }
}

To achieve this i tried creating a provider for my repository which looks like this

export const hyperLedgerRepositoryProvider: Provider = {
  get: async () => {
    const businessNetworkConnection: BusinessNetworkConnection = new BusinessNetworkConnection();
    try {
      const cardName: string = config.get('hyperledger.cardName');
      await businessNetworkConnection.connect(cardName);
      logger.info(`Connected to chain successfully using card name : ${cardName}`);
      return new AgentHyperledgerChainRepository(businessNetworkConnection);
    } catch (err) {
      logger.error(`Error while connecting to chain : ${err}`);
    }
  },
};

But for some reason the injection of my repository into my service always happen after the connection, so i am getting errors like

error: Error creating agent : TypeError: this.chainAgentRepository.createAgent is not a function
info: POST /agent 201 7 - 32.612 ms
error: Error while connecting to chain : Error: Failed to load connector module "composer-connector-hlfv1" for connection type "hlfv1". The gRPC binary module was not installed. This may be fixed by running "npm rebuild"

(The connection error is normal i have not yet initialized my chain). Also my only problem actually comes from the injection of my repository into my services.
I already use the hyperledger package on other projects and it works fine so i am guessing the problem comes from my code ?.

Please tell me if this is unclear. Also how would you deal with injecting repositories connected to other than hyperledger such as databases per example. As i need it to be connected only once at the start.

just a typo

Perhaps you should refactor the class InjectorHanlder to class InjectorHandler just to fix the Typo :)
By the way amazing lean DI. I give it a try in my next Project.

Cheers Michael

Production build ends with error: SyntaxError: Unexpected token: name (isBrowser)

Steps to reproduce are the same as I described some time ago in issue #6 from step 1 to 3.
After that run this in command line:
npm run build
and you'll get an error:

> react-scripts-ts build

Creating an optimized production build...
ts-loader: Using [email protected] and D:\GitHub\efforts\efforts-frontend\tsconfig.json
Failed to compile.

static/js/main.b2af2918.js from UglifyJs
SyntaxError: Unexpected token: name (isBrowser) [./~/typescript-ioc/index.js:3,0]

Multi-injection support

Hey guys! I am heavily using this library in one of my projects, and just ran into a good use case for multi-injection. More specifically, I want to to something like (pseudo code):

  // Provide the implementations, holding them all inside the container
  Container.bind(MyAbstractClass).add(MyConcreteClass1);
  Container.bind(MyAbstractClass).add(MyConcreteClass2);

   // Inject all registered implementations
   constructor(
      @MultiInject private _allImplementations: MyAbstractClass[]
  ) { }
  1. Just to make sure, is there some way to achieve this today that I am missing?
  2. If not, do you guys think it would be a good feature to add?

Let me know your thoughts!

2 Files for abstract class (interface) and implementation result in error

Hi there,

So I used to add abstract class and implemetation in the same file and everything was sweet..
Now if I try to separate the definition to the implementation it fails

eg

afile.ts

export abstract class ITest
{
    public abstract aProperty: string;
}

@Singleton
@Provides(ITest)
export class Test implements ITest
{
    public aProperty: string;
}

will work fine whereas
ITest.ts

export abstract class ITest
{
    public abstract aProperty: string;
}

Test.ts

@Singleton
@Provides(ITest)
export class Test implements ITest
{
    public aProperty: string;
}

will still work but the object is going to be an empty object {}

My guess is that nowhere in my code do I have a link to the Test.ts file, hence not loaded

...
@Inject
protected test: ITest;
...

What I want to achieve -> trying to have a really modular code base, and providing for instance an definition for a Logger but many implementation in a Library. Then the user could choose what implementation to use (ConsoleLogger, CloudwatchLogger, MultiLogger, ...).
I guess then to be able to achieve this I need to use the Container.bing(ITest).to(Test) and won't be able to use decorator.

Is that correct or is there a solution to my problem just using decorators?

Shared container across packages

Hello there,

I am struggling with sharing a container between multiple node packages. I need a package, which is a dependency to the launched package, to register its own singleton services. Then the launched package may register its own services while still being able to use the services registered by the dependency package. Imaging a situation in which we have a core package and an application package using the core functionality.

From looks of the code, this could be achieved by adding ability to somehow "merge" bindings within IoCContainer class, which is internal and cannot be manipulated with.

Any chances of resolving this?

Thank you.

Each type is auto bound

Currently, if we didn't bind type, it will be bound on get method:

    static get(source: Function) {
        const config: ConfigImpl = <ConfigImpl>IoCContainer.bind(source);
        if (!config.iocprovider) {
            config.to(<FunctionConstructor>config.source);
        }
        return config.getInstance();
    }

But I want to return undefined if I forgot to bind a class (i.e. for unit tests). Could you fix to don't use bind() method in get() method()? I don't want to bind if I need just get type.

Export Snapshot in index

Hello,

we use your library in our backends and we like it very much. I just updated to the new 3.0 version and bumped across your Snapshot change. For our unit tests, it would be handy if the Snapshot type would directly be exported by your library.

Right now we must write our tests like this:

import { Container } from "typescript-ioc";
import { Snapshot } from "typescript-ioc/dist/model";

describe("SomeController", () => {
  let controller: TestController;
  let dbService: DatabaseService;
  let dbServiceSnapshot: Snapshot;

  beforeEach(() => {
    dbServiceSnapshot = Container.snapshot(DatabaseService);
    Container.bind(DatabaseService).factory(() => dbService);
    controller = new TestController();
  });

  afterEach(() => {
    dbServiceSnapshot.restore();
  });
});

For convenience it would be cool, if we don't have to import the Snapshot from the dist directory.

Edit:
For bonus points it would be very awesome if the ObjectFactory type would be generic. Our factories look like this:

const configServiceFactory: ObjectFactory = () => {
  const configurationDirs = [path.resolve("config"), path.resolve("config_local")];
  return new ConfigurationService(configurationDirs, true);
};

If we could specify it as const configServiceFactory: ObjectFactory<ConfigurationService> the compiler would tell us, if we return a wrong class for example.

Should I not be using interfaces at all?

In the README.MD there is a section called A note about classes and interfaces which as far as I can tell basically says don't use interfaces, instead use abstract classes with abstract properties/functions.

I just want to clarify that that is the best practise when using typescript-ioc? I.e. I shouldn't have any interfaces in my app?

Example of how to use scopes?

I'm using typescript-ioc in a SPA app. And one of my parent views has data that needs be shown by a child view. What I want to achieve is during the lifetime of the parent view, all child views get the same instance of this data object. But when the parent view is destroyed I need the data object to be removed from DI. That way when the user again navigates to the parent view, there will be a new/fresh instance of the data object added to DI for the children again. And any old instance of the data object just need to go away.

I am thinking that scopes are what I want here, but I'm not sure how to make it work. Can you point me to an example?

decorators order issue

I use this lib with React Native and just faced very strange issue with release version of IOS. (in ios debug and android all works good).

i had next code:

const sessionManagementServiceProvider: Provider = {
  get: () => new SessionManagementService(config),
};

@Singleton
@AutoWired
@Provided(sessionManagementServiceProvider)
...
constructor({ SESSION_KEY }) {
...

to provide config in class constructor and i got next issue:

*** Terminating app due to uncaught exception 'RCTFatalException: Unhandled JS Exception: 
undefined is not an object (evaluating 't.SESSION_KEY')', reason: 'Unhandled JS Exception: 
undefined is not an object (evaluating 't.SESSION..., stack:

I had just to change order of decorators to fix it:

@Provided(sessionManagementServiceProvider)
@Singleton
@AutoWired

works as expected.

Also, my tsconfig, may be something wrong here:

{
  "include": ["./src/"],
  "exclude": [
    "android",
    "ios",
    "node_modules"
  ],
  "compilerOptions": {
    /* Basic Options */
    "target": "es6",                       /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
    "module": "es2015",                       /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
    "lib": [
      "es2017",
      "esnext.asynciterable"
    ],                                        /* Specify library files to be included in the compilation:  */
    // "allowJs": true,                       /* Allow javascript files to be compiled. */
    // "checkJs": true,                       /* Report errors in .js files. */
    "jsx": "react",                           /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
    // "declaration": true,                   /* Generates corresponding '.d.ts' file. */
    "sourceMap": true,                        /* Generates corresponding '.map' file. */
    // "outFile": "./",                       /* Concatenate and emit output to single file. */
    "outDir": "./build",                      /* Redirect output structure to the directory. */
    // "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    // "removeComments": true,                /* Do not emit comments to output. */
    "noEmit": true,                           /* Do not emit outputs. */
    "importHelpers": true,                    /* Import emit helpers from 'tslib'. */

    /* Strict Type-Checking Options */
    "strict": true,                           /* Enable all strict type-checking options. */
    "noImplicitAny": false,                   /* Raise error on expressions and declarations with an implied 'any' type. */
    // "strictNullChecks": true,              /* Enable strict null checks. */
    // "strictFunctionTypes": true,           /* Enable strict checking of function types. */
    // "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */
    // "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */

    /* Additional Checks */
    // "noUnusedLocals": true,                /* Report errors on unused locals. */
    // "noUnusedParameters": true,            /* Report errors on unused parameters. */
    // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
    // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */

    /* Module Resolution Options */
    "moduleResolution": "node",               /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    "skipLibCheck": true,
    "baseUrl": "./src",                       /* Base directory to resolve non-absolute module names. */
    // "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
    // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
    // "typeRoots": [],                       /* List of folders to include type definitions from. */
    // "types": [ "reflect-metadata" ],       /* Type declaration files to be included in compilation. */
    "allowSyntheticDefaultImports": true,     /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
    // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */

    /* Source Map Options */
    // "sourceRoot": "./",                    /* Specify the location where debugger should locate TypeScript files instead of source locations. */
    // "mapRoot": "./",                       /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */
    // "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

    /* Experimental Options */
    "experimentalDecorators": true,           /* Enables experimental support for ES7 decorators. */
    "emitDecoratorMetadata": true,            /* Enables experimental support for emitting type metadata for decorators. */
    "strictPropertyInitialization": false
  }
}

Search for config file does not work on windows

Hello,
I just installed v1.2.0 and my app does not start.
After some debugging I found a culprit in this commit - 3f0a352

It stucks in infite loop because configFile === ROOT never happens.

the ROOT is \ioc.config, but the configFile on windows ends up as C:\ioc.config

PS: Great work with typescript-ioc BTW. From all IOC libraries I found your one is the easiest to use.

Change the value of an injected property is not working

class Test{
   @IoC.Inject abc: number = 123;
}   

const instance: Test = new Test();
const instance2: Test = new Test();
instance2.abc = 234;

expect(instance.abc).to.eq(123);
expect(instance2.abc).to.eq(234);

This test fail.

Use of const breaks minifiers

I really like typescript-ioc but currently the code uses "const". The Typescript compiler doesn't compile this to var. This means that for browser based projects, only >= IE 11 is supported.
I've also found that most Javascript minifiers break on the keyword const. (The popular VisualStudio Minifier & Bundler being one).

Currently I'm having to use my modified version of the library that removes instances of "const".

"TypeError: Invalid type requested to IoC container. Type is not defined." - improve error message?

Been using this library for a while, and it's pretty great compared to the other IoC libraries I've seen. My only major problem is this error - I never have any idea what it's trying to inject and failing to inject. Can this error message be improved so I can track down what it's trying to inject, so I can actually fix it instead of guessing randomly?

Given some advice, I would happily implement this myself as well. I just don't know if this is something that can be done easily or where to approach it, as I've never touched the internals of a DI framework.

@Provides in different file

Hi,

I have issue using your IoC when the abstract class and providing class are in different files.

This is working just fine:

import { Inject, Provides } from "typescript-ioc"

abstract class IFoo {
	abstract bar()
}

@Provides(IFoo)
class Foo implements IFoo {
	bar() {
		console.log("Foo bar");
	}
}

class Worker {
	@Inject
	private foo: IFoo

	work() {
		this.foo.bar()
	}
}

new Worker().work()

However when I split it into the 3 files I get the "TypeError: this.foo.bar is not a function" error:

index.ts:

import { Inject } from "typescript-ioc"
import IFoo from "./IFoo";
import Foo from "./Foo";

class Worker {
	@Inject
	private foo: IFoo

	work() {
		this.foo.bar()
	}
}

new Worker().work()

IFoo.ts:

export default abstract class IFoo {
	abstract bar()
}

Foo.ts:

import { Provides } from "typescript-ioc"
import IFoo from "./IFoo"

@Provides(IFoo)
export default class Foo implements IFoo {
	bar() {
		console.log("Foo bar");
	}
}

Class cannot be invoked without 'new' for target ES6 of compilerOptions

Hi Thiago,

When i tried to run this code with target es6 of compilerOptions I got the error TypeError: Class constructor SimppleInject cannot be invoked without 'new'

import {AutoWired} from "typescript-ioc";
@AutoWired
class SimppleInject {}
const instance: SimppleInject = new SimppleInject();

I use typescript 2.1.5.

tsconfig.json

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "outDir": "./dist",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  },
  "include": [
    "src/**/*.ts",
    "gulp/**/*.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}

Thanks for your IoC!

Invalid type requested to IoC container. Type is not defined. in node js app

I have 2 simple classes and an index.ts entry script.

index.ts looks like this:

import ConfigHelper from './config/ConfigHelper'

const configHelper: ConfigHelper = new ConfigHelper()
configHelper.testOutput()
console.log(configHelper.fsAsyncFactory)

ConfigHelper looks like this:

import {AutoWired, Inject} from 'typescript-ioc'
import FsAsyncFactory from '../fs/FsAsyncFactory'

export class ConfigHelper {

    @Inject
    public fsAsyncFactory: FsAsyncFactory

    public testOutput(): void {
        console.log('### ConfigHelper.testOutput')
    }

}

export default ConfigHelper

FsAsyncFactory looks like this:

import * as BB from 'bluebird'
import * as fs from 'fs'

export class FsAsyncFactory {

    private fsAsync: any = null

    public getFsAsync(): any {

        if (this.fsAsync === null) {
            this.fsAsync = BB.promisifyAll(fs)
        }

        return this.fsAsync
    }

}

export default FsAsyncFactory

But when I run it I get an output of:

### ConfigHelper.testOutput
.../internalprojects/node-di-mocha-test/node_modules/typescript-ioc/es5.js:137
throw new TypeError('Invalid type requested to IoC ' +
^

TypeError: Invalid type requested to IoC container. Type is not defined.
at checkType (.../internalprojects/node-di-mocha-test/node_modules/typescript-ioc/es5.js:137:15)
at Function.IoCContainer.bind (.../internalprojects/node-di-mocha-test/node_modules/typescript-ioc/es5.js:98:9)
at Function.IoCContainer.get (.../internalprojects/node-di-mocha-test/node_modules/typescript-ioc/es5.js:108:35)
at ConfigHelper.get [as fsAsyncFactory] (.../internalprojects/node-di-mocha-test/node_modules/typescript-ioc/es5
                                                                                             .js:119:85)
at Object.<anonymous> (.../internalprojects/node-di-mocha-test/dist/index.js:5:25)
at Module._compile (module.js:624:30)
at Object.Module._extensions..js (module.js:635:10)
at Module.load (module.js:545:32)
at tryModuleLoad (module.js:508:12)
at Function.Module._load (module.js:500:3)

I have a public repo here if you'd like to try for yourself (use this specific commit): adamdry/node-di-mocha-test@1204758

To run the demo:
npm i
npm run dev

I imagine I'm doing something stupid though... :)

Thanks.

get the type registered as provided

Is it possible to expose the type provided like for instance
Container.bind(PersonDAO).to(ManagerDAO);
having a
Container.getType(PersonDAO) would return the ManagerDAO type.

The use case is for instance, i am using class-transformer which requires the type needed when you want to create a object from plain json.

Per Request singletons

for instance use case could be to get the logged in user and save it in a singleton fo the current request. So the dev can just inject the user object and knows it will have the current logged in user.

Look into inRequestScope from InversifyJS

Example of testing?

Can you provide examples of how i can Mock dependency that i @Inject into other classes?

@InRequestScope not returning the same Instance

I'm using typescript-ioc on server side with nodejs and express, and trying to create an object that holds de logged user, that I should be able to access it in any place on my application.
To do so, i've created the following class:

@InRequestScope
@OnlyInstantiableByContainer
class LoggedInUserService { ... }

And i've created an express middleware that would extract the logged user from request, and fill it in LoggedInUserService. But it seems that In the same request, or even in the same function, different instances of LoggedInUserService are retrieve from Container.

function extractUser (req:express.Request, resp: express.Response, next:Function): void {
  const auth = req.headers.authorization
  if (auth != null) {
    const token = auth.replace(/bearer /ig, '')
    const jwtToken = jwt.verify(token, config.jwtSecret) as PIJWTToken
    const loggedInUserService = Container.get(LoggedInUserService)
    loggedInUserService.setup(jwtToken.email)
    const LoggedInUserService2 = Container.get(UsuarioLogadoService)
    
    //the next line is returning false;
    console.log('same instance?', loggedInUserService === loggedInUserService2)
  }
  next()
}

When I use Singleton scope, I do get the same Instance.
Do I need to create some configuration for @InRequestScope ? What am I missing?

Thank you

Cannot overwrite binding with sinon stub

I have a class with an injected property

class MyClass {
    @Inject
     private myProperty: MyDependency
}

Inside of my mocha test I have the following:

Container.bind(MyClass).provider({ get: () => sinon.createStubInstance(MyDependency)});
const myInstance = new MyClass();
myInstance.myMethod(); // throws error because using unmocked version of MyDependency

How can I get this to work?

Dependency injection does not work for instances provided by a linked library (npm link)

DI does not work for libraries which are linked locally via npm link some-lib.

classes provided by some-lib:

  • class A:
import {Singleton} from 'typescript-ioc';

@Singleton
export class A {
    constructor() {
        console.info('A created');
    }

    method() {
        console.info('a: method');
    }
}
  • class B:
import {Inject, Singleton} from 'typescript-ioc';
import {A} from './a';

@Singleton
export class B {
    constructor(@Inject private a: A) {
        console.info('B created, will now call this.a.method()');
        this.a.method();
    }
}

usage in some-app whereas the library is used as a linked library (npm link some-lib):

IMPORTANT: note that the behavior is the same no matter whether the alias is used or not (also see jepetko/some-app@d658f49#diff-aaae91aceb351151eeeb6a1e04817a9c which does not contain any alias)

import alias from 'module-alias';
import path from 'path';
import {Container, Inject} from 'typescript-ioc';

// handle alias properly for the local development (ts-node-dev) and production (node)
const extension = path.extname(process.mainModule.filename);
alias.addAlias('@awesome-lib', path.join(process.cwd(), 'node_modules', 'some-lib', 'dist'));
alias.addAlias('@awesome/app', (extension === '.ts') ? __dirname : `${process.cwd()}/build`);

import {B} from '@awesome-lib/b';

export class Main {

    constructor(@Inject private b: B) {
        console.info('Main injected b: ', this.b);
    }
}

Container.get(Main);

Expected result for npm start (works when the lib is installed via npm i or copied into node_modules):

A created
B created, will now call this.a.method()
a: method
Main injected b:  B {
  a:
   A { __BuildContext: ContainerBuildContext { context: Map {} } },
  __BuildContext: ContainerBuildContext { context: Map {} } }

Actual result for npm start (does not work when the lib is installed via npm link some-lib):

B created, will now call this.a.method()
C:\data\workspaces\dist\b.js:21
        this.a.method();
               ^

TypeError: Cannot read property 'method' of undefined
    at new B (C:\data\workspaces\some-lib\dist\b.js:21:16)
    at factory (C:\data\workspaces\some-app\node_modules\typescript-ioc\dist\container\container-binding-config.js:21:63)
    at iocFactory (C:\data\workspaces\some-app\node_modules\typescript-ioc\dist\container\container-binding-config.js:36:30)
    at LocalScope.resolve (C:\data\workspaces\some-app\node_modules\typescript-ioc\dist\scopes.js:10:16)
    at IoCBindConfig.getInstance (C:\data\workspaces\some-app\node_modules\typescript-ioc\dist\container\container-binding-config.js:71:30)
    at IoCBindConfig.get [as instanceFactory] (C:\data\workspaces\some-app\node_modules\typescript-ioc\dist\container\container.js:46:23)
    at paramTypes.map.paramType (C:\data\workspaces\some-app\node_modules\typescript-ioc\dist\container\container-binding-config.js:88:29)
    at Array.map (<anonymous>)
    at IoCBindConfig.getParameters (C:\data\workspaces\some-app\node_modules\typescript-ioc\dist\container\container-binding-config.js:84:36)
    at factory (C:\data\workspaces\some-app\node_modules\typescript-ioc\dist\container\container-binding-config.js:19:37)
  • OS: Windows
  • Node version: v10.15.3
  • npm version: 6.4.1
  • typescript version: 3.8.3
  • typescript-ioc version: 3.2.1

Sources

Container.bind doesn't work in IE

... but works fine in Edge and Chrome.

I've created a repo to reproduce the problem here: https://github.com/drwatson1/typescript-ioc-example

If you'll try to open the example in IE you'll see these errors:

InjectorHanlder.getConstructorFromType
D:\GitHub\typescript-ioc-example\node_modules\typescript-ioc\es5.js:288
  285 |                 return typeConstructor;
  286 |             }
  287 |         }
> 288 |         throw TypeError('Can not identify the base Type for requested target');
  289 |     };
  290 |     return InjectorHanlder;
  291 | }());

IoCContainer.isBound
D:\GitHub\typescript-ioc-example\node_modules\typescript-ioc\es5.js:91
  88 | }
  89 | IoCContainer.isBound = function (source) {
  90 |     checkType(source);
> 91 |     var baseSource = InjectorHanlder.getConstructorFromType(source);
  92 |     var config = IoCContainer.bindings.get(baseSource);
  93 |     return (!!config);
  94 | };

Container.bind
D:\GitHub\typescript-ioc-example\node_modules\typescript-ioc\es5.js:74
  71 | function Container() {
  72 | }
  73 | Container.bind = function (source) {
> 74 |     if (!IoCContainer.isBound(source)) {
  75 |         AutoWired(source);
  76 |         return IoCContainer.bind(source).to(source);
  77 |     }

Anonymous function
D:\GitHub\typescript-ioc-example\src\ioc.ts:14
  11 | /// 
  12 | const Container = require('typescript-ioc/es5.js').Container;
  13 | 
> 14 | Container.bind(SomeService).to(SomeServiceImpl);
  15 | 
  16 | export {
  17 |     SomeService,

... and so on.

Property X is used before it is assigned.

In the following code (tsoa framework)

@Route('account')
export class DevAccount extends Controller {
    @Inject
    private dependencies: DevAccountDependencies

    constructor(private readonly config = this.dependencies.config,
                private readonly secrets = this.dependencies.secrets) {
        super()
    }
    ...

I get Property 'dependencies' has no initializer and is not definitely assigned in the constructor, and Property 'dependencies' is used before being assigned errors.

How is this form of dependency injection meant to work in typescript?

Error thrown when trying to getType() of a unregistered type

(node:137) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'targetSource' of undefined
    at Function.IoCContainer.getType (/home/coolerminers/node_modules/typescript-ioc/es5.js:143:23)
    at Function.Container.getType (/home/coolerminers/node_modules/typescript-ioc/es5.js:86:29)

Webpack transpiler error with UglifyJsPlugin

webpack.conf.js

plugins: plugins,
    optimization: {
    minimizer: [
        new UglifyJsPlugin({
            uglifyOptions: {
                output: {
                    comments: false
                }
            }
        })
    ]
},

and webpack --mode=production
fails with

ERROR in xyz.js from UglifyJs
Unexpected token: keyword «const» [xyz:4540,4]

Removing the plugin works well.

Allow for removing/reverting bindings

When running unit tests, I force the binding for an interface to point to a mock implementation of it. (I override the normal annotation bindings.) Other test suites, should use the default bindings. The result is that the tests in the later suites are failing when run as a batch, but run fine when only the specific suite is run.
Is there a way to reset the bindings back to the way they were? My thought is:

  1. in before, we grab the existing configurations that we are about to override.
  2. Run the tests
  3. in after, put the configurations back.

Unfortunately, there is no way to get the current configuration, only the actual instance to determine what was originally set there.

Async Factory

Hi all! I want to perform this factory class behaviour, but when I call new S(), a promise is returned. Do I do something wrong? May this feature is not present for being used as factory?

const factory: ObjectFactory = async () => S.getInstance();

@Factory(factory)
export class S {

  private static _instance: S;

  static async getInstance(): Promise<S> {
    if (!this._instance) {
      this._instance = new S();

      await this._instance.createClient();
    }

    return this._instance;
  }

  async createClient(): Promise<void> {
    this._client = await createClientAsync('url');
  }

}

Thank you all in advance!

@InjectValue annotation is not correctly exported

I wanted to use the @InjectValue annotation like in the documentation, but is was not exported at the module level like with @Inject and other annotations.
So currently I must import it from 'typescript-ioc/dist/decorators' but I don't think it's the intended way.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.