thiagobustamante / typescript-ioc Goto Github PK
View Code? Open in Web Editor NEWA Lightweight annotation-based dependency injection container for typescript.
License: MIT License
A Lightweight annotation-based dependency injection container for typescript.
License: MIT License
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.
could you please create and update changelog for the releases? Would be really helpfull
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
(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)
... 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.
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
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.
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?
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");
}
}
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.
In the documentation you mentioned that interfaces cannot be used: https://github.com/thiagobustamante/typescript-ioc#a-note-about-classes-and-interfaces
I have not looked at the implementation details, but InversifyJS does allow using interfaces.
What is the key difference between the two frameworks?
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.
constructor(@Inject protected myObject: MyObject)
{
super(myObject);
}
myObject is undefined.
Is there a way to set constants to container?
For example
const config:IConfig = {
connectionString: "xpto"
};
Container.bind(config).provider(config).
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.
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?
Just wondering if this is possible.
Couldn't find an example anywhere..
Thanks
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"
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.
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.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.
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.
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]
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
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.
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?
import {Inject, AutoWired} from 'typescript-ioc/es5'
webstorm report error:
TS7016: Could not find a declaration file for module 'typescript-ioc/es5'
What about native Map<T>
class? (typescript-ioc.ts#L229)
Now you have string representation of constructors as key... It is not good, I guess...
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?
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:
Unfortunately, there is no way to get the current configuration, only the actual instance to determine what was originally set there.
Please could you explain or show a case why @Autowired is meaningful except @singleton?
Can you provide examples of how i can Mock dependency that i @Inject
into other classes?
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);
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
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.
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?
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?
I end up with: Uncaught Error: Cannot find module './es5'
If I change the output from:
module.exports = require(useES6 ? './es6' : './es5');
to:
module.exports = require('./es5');
Then everything works fine.
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.
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!
DI does not work for libraries which are linked locally via npm link some-lib
.
classes provided by some-lib
:
import {Singleton} from 'typescript-ioc';
@Singleton
export class A {
constructor() {
console.info('A created');
}
method() {
console.info('a: method');
}
}
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)
Sources
I've got an error "Uncaught TypeError: fs.existsSync is not a function" in Chrome.
Steps to reproduce:
create-react-app app1 --scripts-version=react-scripts-ts
cd app1
npm install --save typescript-ioc
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
All other options leave as they are.
import { Container } from 'typescript-ioc';
Container.bind(Date).to(Date);
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"
]
}
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[]
) { }
Let me know your thoughts!
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.
As I can understand index.js intended to be used in both es5 and es6.
If so, you can't use let and const there because they were introduced in es6 only.
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.
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
}
}
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!
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:
super(...args)
succeeds@OnlyInstantiableByContainer
instantiation happens during this callInjectorHandler.assertInstantiable()
failsFull 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
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".
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.