Coder Social home page Coder Social logo

thiagobustamante / typescript-ioc Goto Github PK

View Code? Open in Web Editor NEW
525.0 13.0 64.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 Issues

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.

CHANGELOG

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

@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

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)

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.

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

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.

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?

@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");
	}
}

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.

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.

Set constants

Is there a way to set constants to container?

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

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

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.

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?

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"

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.

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

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.

@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.

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]

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

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.

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?

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?

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.

Example of testing?

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

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);

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

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.

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?

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?

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.

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!

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

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"
  ]
}

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!

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.

"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.

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
  }
}

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!

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

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".

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.