Giter Site home page Giter Site logo

microsoft / tsyringe Goto Github PK

View Code? Open in Web Editor NEW
4.9K 43.0 159.0 1.31 MB

Lightweight dependency injection container for JavaScript/TypeScript

License: MIT License

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

tsyringe's Introduction

Travis npm npm

TSyringe

A lightweight dependency injection container for TypeScript/JavaScript for constructor injection.

Installation

Install by npm

npm install --save tsyringe

or install with yarn (this project is developed using yarn)

yarn add tsyringe

Modify your tsconfig.json to include the following settings

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

Add a polyfill for the Reflect API (examples below use reflect-metadata). You can use:

The Reflect polyfill import should only be added once, and before DI is used:

// main.ts
import "reflect-metadata";

// Your code here...

Babel

If you're using Babel (e.g. using React Native), you will need to configure it to emit TypeScript metadata.

First get the Babel plugin

Yarn

yarn add --dev babel-plugin-transform-typescript-metadata

npm

npm install --save-dev babel-plugin-transform-typescript-metadata

Then add it to your Babel config

plugins: [
            'babel-plugin-transform-typescript-metadata',
            /* ...the rest of your config... */
         ]

API

TSyringe performs Constructor Injection on the constructors of decorated classes.

Decorators

injectable()

Class decorator factory that allows the class' dependencies to be injected at runtime. TSyringe relies on several decorators in order to collect metadata about classes to be instantiated.

Usage

import {injectable} from "tsyringe";

@injectable()
class Foo {
  constructor(private database: Database) {}
}

// some other file
import "reflect-metadata";
import {container} from "tsyringe";
import {Foo} from "./foo";

const instance = container.resolve(Foo);

singleton()

Class decorator factory that registers the class as a singleton within the global container.

Usage

import {singleton} from "tsyringe";

@singleton()
class Foo {
  constructor() {}
}

// some other file
import "reflect-metadata";
import {container} from "tsyringe";
import {Foo} from "./foo";

const instance = container.resolve(Foo);

autoInjectable()

Class decorator factory that replaces the decorated class' constructor with a parameterless constructor that has dependencies auto-resolved.

Note Resolution is performed using the global container.

Usage

import {autoInjectable} from "tsyringe";

@autoInjectable()
class Foo {
  constructor(private database?: Database) {}
}

// some other file
import {Foo} from "./foo";

const instance = new Foo();

Notice how in order to allow the use of the empty constructor new Foo(), we need to make the parameters optional, e.g. database?: Database.

inject()

Parameter decorator factory that allows for interface and other non-class information to be stored in the constructor's metadata.

Usage

import {injectable, inject} from "tsyringe";

interface Database {
  // ...
}

@injectable()
class Foo {
  constructor(@inject("Database") private database?: Database) {}
}

injectAll()

Parameter decorator for array parameters where the array contents will come from the container. It will inject an array using the specified injection token to resolve the values.

Usage

import {injectable, injectAll} from "tsyringe";

@injectable()
class Foo {}

@injectable()
class Bar {
  constructor(@injectAll(Foo) fooArray: Foo[]) {
    // ...
  }
}

injectWithTransform()

Parameter decorator which allows for a transformer object to take an action on the resolved object before returning the result.

class FeatureFlags {
  public getFlagValue(flagName: string): boolean {
    // ...
}

class Foo() {}

class FeatureFlagsTransformer implements Transform<FeatureFlags, bool> {
  public transform(flags: FeatureFlags, flag: string) {
    return flags.getFlagValue(flag);
  }
}

@injectable()
class MyComponent(foo: Foo, @injectWithTransform(FeatureFlags, FeatureFlagsTransformer, "IsBlahEnabled") blahEnabled: boolean){
  // ...
}

injectAllWithTransform()

This parameter decorator allows for array contents to be passed through a transformer. The transformer can return any type, so this can be used to map or fold an array.

@injectable()
class Foo {
  public value;
}

class FooTransform implements Transform<Foo[], string[]>{
  public transform(foos: Foo[]): string[]{
    return foos.map(f => f.value));
  }
}

@injectable()
class Bar {
  constructor(@injectAllWithTransform(Foo, FooTransform) stringArray: string[]) {
    // ...
  }
}

scoped()

Class decorator factory that registers the class as a scoped dependency within the global container.

Available scopes

  • Transient
    • The default registration scope, a new instance will be created with each resolve
  • Singleton
    • Each resolve will return the same instance (including resolves from child containers)
  • ResolutionScoped
    • The same instance will be resolved for each resolution of this dependency during a single resolution chain
  • ContainerScoped
    • The dependency container will return the same instance each time a resolution for this dependency is requested. This is similar to being a singleton, however if a child container is made, that child container will resolve an instance unique to it.

Usage

@scoped(Lifecycle.ContainerScoped)
class Foo {}

Container

The general principle behind Inversion of Control (IoC) containers is you give the container a token, and in exchange you get an instance/value. Our container automatically figures out the tokens most of the time, with 2 major exceptions, interfaces and non-class types, which require the @inject() decorator to be used on the constructor parameter to be injected (see above).

In order for your decorated classes to be used, they need to be registered with the container. Registrations take the form of a Token/Provider pair, so we need to take a brief diversion to discuss tokens and providers.

Injection Token

A token may be either a string, a symbol, a class constructor, or a instance of DelayedConstructor.

type InjectionToken<T = any> =
  | constructor<T>
  | DelayedConstructor<T>
  | string
  | symbol;

Providers

Our container has the notion of a provider. A provider is registered with the DI container and provides the container the information needed to resolve an instance for a given token. In our implementation, we have the following 4 provider types:

Class Provider

{
  token: InjectionToken<T>;
  useClass: constructor<T>;
}

This provider is used to resolve classes by their constructor. When registering a class provider you can simply use the constructor itself, unless of course you're making an alias (a class provider where the token isn't the class itself).

Value Provider

{
  token: InjectionToken<T>;
  useValue: T
}

This provider is used to resolve a token to a given value. This is useful for registering constants, or things that have a already been instantiated in a particular way.

Factory provider

{
  token: InjectionToken<T>;
  useFactory: FactoryFunction<T>;
}

This provider is used to resolve a token using a given factory. The factory has full access to the dependency container.

We have provided 2 factories for you to use, though any function that matches the FactoryFunction<T> signature can be used as a factory:

type FactoryFunction<T> = (dependencyContainer: DependencyContainer) => T;
instanceCachingFactory

This factory is used to lazy construct an object and cache result, returning the single instance for each subsequent resolution. This is very similar to @singleton()

import {instanceCachingFactory} from "tsyringe";

{
  token: "SingletonFoo";
  useFactory: instanceCachingFactory<Foo>(c => c.resolve(Foo));
}
instancePerContainerCachingFactory

This factory is used to lazy construct an object and cache result per DependencyContainer, returning the single instance for each subsequent resolution from a single container. This is very similar to @scoped(Lifecycle.ContainerScoped)

import {instancePerContainerCachingFactory} from "tsyringe";

{
  token: "ContainerScopedFoo";
  useFactory: instancePerContainerCachingFactory<Foo>(c => c.resolve(Foo));
}
predicateAwareClassFactory

This factory is used to provide conditional behavior upon resolution. It caches the result by default, but has an optional parameter to resolve fresh each time.

import {predicateAwareClassFactory} from "tsyringe";

{
  token: "FooHttp",
  useFactory: predicateAwareClassFactory<Foo>(
    c => c.resolve(Bar).useHttps, // Predicate for evaluation
    FooHttps, // A FooHttps will be resolved from the container if predicate is true
    FooHttp // A FooHttp will be resolved if predicate is false
  );
}

Token Provider

{
  token: InjectionToken<T>;
  useToken: InjectionToken<T>;
}

This provider can be thought of as a redirect or an alias, it simply states that given token x, resolve using token y.

Register

The normal way to achieve this is to add DependencyContainer.register() statements somewhere in your program some time before your first decorated class is instantiated.

container.register<Foo>(Foo, {useClass: Foo});
container.register<Bar>(Bar, {useValue: new Bar()});
container.register<Baz>("MyBaz", {useValue: new Baz()});

Registration options

As an optional parameter to .register() you may provide RegistrationOptions which customize how the registration behaves. See the linked source code for up to date documentation on available options.

Registry

You can also mark up any class with the @registry() decorator to have the given providers registered upon importing the marked up class. @registry() takes an array of providers like so:

@registry([
  { token: Foobar, useClass: Foobar },
  { token: "theirClass", useFactory: (c) => {
       return new TheirClass( "arg" )
    },
  }
])
class MyClass {}

This is useful when you want to register multiple classes for the same token. You can also use it to register and declare objects that wouldn't be imported by anything else, such as more classes annotated with @registry or that are otherwise responsible for registering objects. Lastly you might choose to use this to register 3rd party instances instead of the container.register(...) method. note: if you want this class to be @injectable you must put the decorator before @registry, this annotation is not required though.

Resolution

Resolution is the process of exchanging a token for an instance. Our container will recursively fulfill the dependencies of the token being resolved in order to return a fully constructed object.

The typical way that an object is resolved is from the container using resolve().

const myFoo = container.resolve(Foo);
const myBar = container.resolve<Bar>("Bar");

You can also resolve all instances registered against a given token with resolveAll().

interface Bar {}

@injectable()
class Foo implements Bar {}
@injectable()
class Baz implements Bar {}

@registry([
  // registry is optional, all you need is to use the same token when registering
  {token: "Bar", useToken: Foo}, // can be any provider
  {token: "Bar", useToken: Baz}
])
class MyRegistry {}

const myBars = container.resolveAll<Bar>("Bar"); // myBars type is Bar[]

Interception

Interception allows you to register a callback that will be called before or after the resolution of a specific token. This callback can be registered to execute only once (to perform initialization, for example), on each resolution to do logging, for example.

beforeResolution is used to take an action before an object is resolved.

class Bar {}

container.beforeResolution(
  Bar,
  // Callback signature is (token: InjectionToken<T>, resolutionType: ResolutionType) => void
  () => {
    console.log("Bar is about to be resolved!");
  },
  {frequency: "Always"}
);

afterResolution is used to take an action after the object has been resolved.

class Bar {
  public init(): void {
    // ...
  }
}

container.afterResolution(
  Bar,
  // Callback signature is (token: InjectionToken<T>, result: T | T[], resolutionType: ResolutionType)
  (_t, result) => {
    result.init();
  },
  {frequency: "Once"}
);

Child Containers

If you need to have multiple containers that have disparate sets of registrations, you can create child containers:

const childContainer1 = container.createChildContainer();
const childContainer2 = container.createChildContainer();
const grandChildContainer = childContainer1.createChildContainer();

Each of the child containers will have independent registrations, but if a registration is absent in the child container at resolution, the token will be resolved from the parent. This allows for a set of common services to be registered at the root, with specialized services registered on the child. This can be useful, for example, if you wish to create per-request containers that use common stateless services from the root container.

Clearing Instances

The container.clearInstances() method allows you to clear all previously created and registered instances:

class Foo {}
@singleton()
class Bar {}

const myFoo = new Foo();
container.registerInstance("Test", myFoo);
const myBar = container.resolve(Bar);

container.clearInstances();

container.resolve("Test"); // throws error
const myBar2 = container.resolve(Bar); // myBar !== myBar2
const myBar3 = container.resolve(Bar); // myBar2 === myBar3

Unlike with container.reset(), the registrations themselves are not cleared. This is especially useful for testing:

@singleton()
class Foo {}

beforeEach(() => {
  container.clearInstances();
});

test("something", () => {
  container.resolve(Foo); // will be a new singleton instance in every test
});

Circular dependencies

Sometimes you need to inject services that have cyclic dependencies between them. As an example:

@injectable()
export class Foo {
  constructor(public bar: Bar) {}
}

@injectable()
export class Bar {
  constructor(public foo: Foo) {}
}

Trying to resolve one of the services will end in an error because always one of the constructor will not be fully defined to construct the other one.

container.resolve(Foo);
Error: Cannot inject the dependency at position #0 of "Foo" constructor. Reason:
    Attempted to construct an undefined constructor. Could mean a circular dependency problem. Try using `delay` function.

The delay helper function

The best way to deal with this situation is to do some kind of refactor to avoid the cyclic dependencies. Usually this implies introducing additional services to cut the cycles.

But when refactor is not an option you can use the delay function helper. The delay function wraps the constructor in an instance of DelayedConstructor.

The delayed constructor is a kind of special InjectionToken that will eventually be evaluated to construct an intermediate proxy object wrapping a factory for the real object.

When the proxy object is used for the first time it will construct a real object using this factory and any usage will be forwarded to the real object.

@injectable()
export class Foo {
  constructor(@inject(delay(() => Bar)) public bar: Bar) {}
}

@injectable()
export class Bar {
  constructor(@inject(delay(() => Foo)) public foo: Foo) {}
}

// construction of foo is possible
const foo = container.resolve(Foo);

// property bar will hold a proxy that looks and acts as a real Bar instance.
foo.bar instanceof Bar; // true

Interfaces and circular dependencies

We can rest in the fact that a DelayedConstructor could be used in the same contexts that a constructor and will be handled transparently by tsyringe. Such idea is used in the next example involving interfaces:

export interface IFoo {}

@injectable()
@registry([
  {
    token: "IBar",
    // `DelayedConstructor` of Bar will be the token
    useToken: delay(() => Bar)
  }
])
export class Foo implements IFoo {
  constructor(@inject("IBar") public bar: IBar) {}
}
export interface IBar {}

@injectable()
@registry([
  {
    token: "IFoo",
    useToken: delay(() => Foo)
  }
])
export class Bar implements IBar {
  constructor(@inject("IFoo") public foo: IFoo) {}
}

Disposable instances

All instances created by the container that implement the Disposable interface will automatically be disposed of when the container is disposed.

container.dispose();

or to await all asynchronous disposals:

await container.dispose();

Full examples

Example without interfaces

Since classes have type information at runtime, we can resolve them without any extra information.

// Foo.ts
export class Foo {}
// Bar.ts
import {Foo} from "./Foo";
import {injectable} from "tsyringe";

@injectable()
export class Bar {
  constructor(public myFoo: Foo) {}
}
// main.ts
import "reflect-metadata";
import {container} from "tsyringe";
import {Bar} from "./Bar";

const myBar = container.resolve(Bar);
// myBar.myFoo => An instance of Foo

Example with interfaces

Interfaces don't have type information at runtime, so we need to decorate them with @inject(...) so the container knows how to resolve them.

// SuperService.ts
export interface SuperService {
  // ...
}
// TestService.ts
import {SuperService} from "./SuperService";
export class TestService implements SuperService {
  //...
}
// Client.ts
import {injectable, inject} from "tsyringe";

@injectable()
export class Client {
  constructor(@inject("SuperService") private service: SuperService) {}
}
// main.ts
import "reflect-metadata";
import {Client} from "./Client";
import {TestService} from "./TestService";
import {container} from "tsyringe";

container.register("SuperService", {
  useClass: TestService
});

const client = container.resolve(Client);
// client's dependencies will have been resolved

Injecting primitive values (Named injection)

Primitive values can also be injected by utilizing named injection

import {singleton, inject} from "tsyringe";

@singleton()
class Foo {
  private str: string;
  constructor(@inject("SpecialString") value: string) {
    this.str = value;
  }
}

// some other file
import "reflect-metadata";
import {container} from "tsyringe";
import {Foo} from "./foo";

const str = "test";
container.register("SpecialString", {useValue: str});

const instance = container.resolve(Foo);

Non goals

The following is a list of features we explicitly plan on not adding:

  • Property Injection

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.

tsyringe's People

Contributors

aweppc avatar cypherix93 avatar daniel-white avatar dependabot[bot] avatar emilioastarita avatar jamarzka avatar jorgelnjunior avatar kootoopas avatar mauvm avatar megahertz avatar meltingmosaic avatar mgred avatar microsoft-github-policy-service[bot] avatar microsoftopensource avatar mikl avatar moln avatar msftgits avatar raymondbenc avatar rbgmulmb avatar skiptirengu avatar xapphire13 avatar xenoterracide 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  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

tsyringe's Issues

Allow multiple implementations of an interface

Something I really miss on tsyringe is the ability to register multiple implementations to a single interface, like the following:

interface Task {
    run(): void;
}

class TaskOne implements Task {
    public run(): void {
        console.log('1');
    }
}

class TaskTwo implements Task {
    public run(): void {
        console.log('2');
    }
}

container.register<Task>('Task', TaskOne);
container.register<Task>('Task', TaskTwo);

// resolveAll returns an array with instances of all types bound to the 'Task' interface
container.resolveAll<Task>('Task').forEach((task): void => task.run());

About factory provider design, why can not direct set as singleton.

class FooList {
  public void add(str: string) {}
}
di.register<FooList>(
  FooList, 
  {
    useFactory: (d) => {
      const obj = new FooList();
      obj.add('foo1');
      return obj;
    }
  },
 {singleton: true}
); 

const result1 = di.resolve<FooList>(FooList);
const result2 = di.resolve<FooList>(FooList);

console.log(result1 === result2); 

Expected Behavior

Console result: true

Current Behavior

Throw: Cannot use {singleton: true} with ValueProviders or FactoryProviders

Solution

Use TokenProvider again.

di.register<FooList>(
  'FooListFactory', 
  {
    useFactory: (d) => {
      const obj = new FooList();
      obj.add('foo1');
      return obj;
    }
  }
);
di.register<FooList>(
  FooList, 
  {useToken:  'FooListFactory',},
  {singleton: true}
);

But, why can not direct set it.

Is it a bug ? Or some problem of design?

@autoInjectable() and @singleton() can't use together.

I have one class like this.

@autoInjectable()
@singleton()
export class AppEventEmitterService extends EventEmitter {
    private readonly logger: Logger

    constructor(private loggerService?: LoggerService) {
...

I created like this because I need to log on each event that emit and this class will using across of app. Problem that I found is this class will don't be singleton if I add autoInjectable. Right now I need to create logger as const variable outside of class and using from that.

Just ask at here if someone have same problem like me.

[Documentation]: some improvement suggestions

Hi!
Awesome DI framework.
Thanks for your time ๐Ÿ˜„

Is your feature request related to a problem? Please describe.
Missing Docs.

Description
Improve documentation.
I have a look at docs & I think there are some improvements:

Alternate solutions
N/A
Additional context
N/A

export DependencyContainer type

I have this in several files where I configure via factory methods,

import DependencyContainer from 'tsyringe/dist/typings/types/dependency-container';

would be nice if I could just do import DependencyContainer from 'tsyringe' example

@registry([
  { token: InjectToken.ApolloProvider, useClass: ApolloProvider },
  { token: InjectToken.ContentfulProvider, useClass: ContentfulProvider },
  { token: InjectToken.StripeProvider, useClass: StripeProvider },
])
export default class BootStrapper {

  constructor(private readonly container: DependencyContainer) {
  }

  async registerAll(): Promise<void> {
    await new RegistryConfigurer(this.container).registerAll();
  }

  async getApolloServer(): Promise<ApolloServer> {
    await this.registerAll();
    return this.container.resolve<ApolloServer>(InjectToken.ApolloServer);
  }
}

Uncaught TypeInfo not known for function Bar(foo)

I created a react app with --typescript
install tsyringe and reflect-metadata and got this error

Uncaught TypeInfo not known for function Bar(foo)
import {container, injectable} from "tsyringe"

export class Foo {}

@injectable()
export class Bar {
  constructor(public foo: Foo) {}
}

export const myBar = container.resolve(Bar);

Option for Disabling Lazy Loading

Is there an option to disable lazy initialization on instances?
It could be useful if the objects are initialized at start, not on first usage.

I may have missed something but i can not see this option.

Thanks a lot!

How to handle several container scenarios?

Is your feature request related to a problem? Please describe.
I have a use case where I need to be able to manage multiple containers each with specific settings. By settings I mean a different object to inject to the last nested object.

Description
To do so It would be very great if we could instantiate containers this way:

let container1 = new Container();
let container2 = new Container();

And then "attach" the settings object (in my use case) to containers like this:

container1.register("SettingsClass", {
  provider: () => settingsObject1
});
container2.register("SettingsClass", {
  provider: () => settingsObject2
});

So when resolving objects the right settingsObject1 will be injected and successfully get

const myService1 = container1.resolve(MyService);
const myService2 = container2.resolve(MyService);

Maybe there is already a way to do it but I could not find it, could you help me?
PS: I'm a noob in typescript :)

Hierarchy of injections not possible

It seems that building a hierarchy of injections is not possible. For example:

Service.ts

export interface Service {}

ServiceImpl.ts

import { Service } from "./Service";

export class ServiceImpl implements Service {}

Client.ts

export interface Client {}

ClientImpl.ts

import { inject } from "tsyringe";
import { Client } from "./Client";
import { Service } from "./Service";

export class ClientImpl implements Client {
  constructor(@inject("Service") service: Service) {}
}

CdiConfi.ts

import "reflect-metadata";
import { ServiceImpl } from "./ServiceImpl";
import { Service } from "./Service";
import { ClientImpl } from "./ClientImpl";
import { Client } from "./Client";
import { container } from "tsyringe";

container.register("Service", { useClass: ServiceImpl });
container.register("Client", { useClass: ClientImpl });

const service = container.resolve<Service>("Service");
console.log(service);

const client = container.resolve<Client>("Client");
console.log(client);

This configuration is not working. I get the following exception:

throw `TypeInfo not known for ${ctor}`;
            ^
TypeInfo not known for class ClientImpl {
    constructor(service) { }
}

What would I expect in the CdiConfig.ts?
To have a resolved ClientImpl in the CdiConfig.ts for the Client-Interface with all its dependencies being resolved recursively, like in InversifyJS for example. Is this not possible with this framework?

factories resolution scope

  • ResolutionScoped
  • The same instance will be resolved for each resolution of this dependency during a single
    resolution chain

are factories resolution scoped? or will more than one thing be resolved if the factory is not cached?

doc bug, will fix with clarity

Allow @inject() to use with property.

Is your feature request related to a problem? Please describe.

class Child extends Parent {
  constructor(@inject('something') private something: string) {
    super(); // This is required since this class has Parent. 
  }
}

It's not convenient to describe deps in constructor if i have to call super(). I'd want to inject deps right in property

Alternate solutions

class Child extends Parent {
  @inject('something') private something: string
}

Much more cleaner

Add `.clearInstances()` method

Description
When unit testing, it is nice to have a clean slate between each test, for this we have the .reset() method. However, it is useful in some cases to not lose the registrations between tests, just the cached instances (see #28 (comment)).

A solution to this would be to add a method like .clearInstances() which keeps registrations, but clears the cached instances.

Alternate solutions

  • Bespoke instance clearing logic that involves a public consumer using internal knowledge of the container
  • Using .reset() and having a beforeEach() method to rehydrate the registrations

improve exceptions

hmmm... getting this in a jest test...

import { ApolloServerTestClient } from 'apollo-server-testing';
import 'reflect-metadata';
import testContext from '../test-fixtures/testContext';
import { Beans } from '../types/Beans';

test('version query', async() => {
  const { query } = await testContext()
    .resolve<Promise<ApolloServerTestClient>>(Beans.APOLLO_TEST_CLIENT);
  const res = await query({ query: '{ version }' });
  expect(res.data).toEqual({ version: 'dev' });
});

it's complaining at test

Error: Failed: "TypeInfo not known for function Object() { [native code] }"
Error: 
    at Env.it (/Users/calebcushing/IdeaProjects/service-graph/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:91:24)
    at it (/Users/calebcushing/IdeaProjects/service-graph/node_modules/jest-jasmine2/build/jasmine/jasmineLight.js:93:21)
    at Object.<anonymous> (/Users/calebcushing/IdeaProjects/service-graph/src/__tests__/VersionTest.ts:6:1)
    at Runtime._execModule (/Users/calebcushing/IdeaProjects/service-graph/node_modules/jest-runtime/build/index.js:867:68)
    at Runtime._loadModule (/Users/calebcushing/IdeaProjects/service-graph/node_modules/jest-runtime/build/index.js:577:12)
    at Runtime.requireModule (/Users/calebcushing/IdeaProjects/service-graph/node_modules/jest-runtime/build/index.js:433:10)
    at /Users/calebcushing/IdeaProjects/service-graph/node_modules/jest-jasmine2/build/index.js:202:13
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (/Users/calebcushing/IdeaProjects/service-graph/node_modules/jest-jasmine2/build/index.js:27:24)
    at _next (/Users/calebcushing/IdeaProjects/service-graph/node_modules/jest-jasmine2/build/index.js:47:9)

the problem was I didn't include @inject("tag") in my dependencies.

For this ticket I'd like to see an improved exception. At the very least I need to know which class it was trying to inject when it failed, ideally with the type or parameter position trying to be injected, as I had about a dozen. At best I'd like to see the actual source of the resolve in the stacktrace.

Cannot read property 'length' of undefined

Describe the bug
When I have a class with an empty constructor container blows up. I have a class which requires the CLASS (below).

This causes the whole container to blow up with an undefined.length issue.

 InternalDependencyContainer.prototype.resolve = function (token) { <--- token is undefined
        console.info("TOKEN", token)
        var registration = this.getRegistration(token);
        console.info("REGISTRATION", registration)
        if (!registration && isNormalToken(token)) {
            throw "Attempted to resolve unregistered dependency token: " + token.toString();
        }
        if (registration) {
            return this.resolveRegistration(registration);
        }
        return this.construct(token);
    };

To Reproduce

CLASS Causing the issue

@singleton()
export class EventService {
    private config: EventConfig
    private sns: SNS

    public constructor() {
        this.config = eventConfig
        this.sns = new SNS({
            endpoint: eventConfig.endpoint,
        })
    }

    public async publish(event: Event): Promise<void> {
        const opts: PublishInput = {
            Message: JSON.stringify(event),
            TopicArn: this.config.topicArn,
            Subject: event.type,
        }
        await this.sns.publish(opts).promise()
    }
}

ERROR:


TypeError: Cannot read property 'length' of undefined
    at InternalDependencyContainer.../../../node_modules/tsyringe/dist/esm5/dependency-container.js.InternalDependencyContainer.construct (/Users/flowers_ben/Projects/ortus/ortus-monorepo/packages/api/client/.webpack/service/src/handlers/companies/webpack:/Users/flowers_ben/Projects/ortus/ortus-monorepo/node_modules/tsyringe/dist/esm5/dependency-container.js:133:1)
    at InternalDependencyContainer.../../../node_modules/tsyringe/dist/esm5/dependency-container.js.InternalDependencyContainer.resolve (/Users/flowers_ben/Projects/ortus/ortus-monorepo/packages/api/client/.webpack/service/src/handlers/companies/webpack:/Users/flowers_ben/Projects/ortus/ortus-monorepo/node_modules/tsyringe/dist/esm5/dependency-container.js:67:1)
    at /Users/flowers_ben/Projects/ortus/ortus-monorepo/packages/api/client/.webpack/service/src/handlers/companies/webpack:/Users/flowers_ben/Projects/ortus/ortus-monorepo/node_modules/tsyringe/dist/esm5/dependency-container.js:147:1
    at Array.map (<anonymous>)
    at InternalDependencyContainer.../../../node_modules/tsyringe/dist/esm5/dependency-container.js.InternalDependencyContainer.construct (/Users/flowers_ben/Projects/ortus/ortus-monorepo/packages/api/client/.webpack/service/src/handlers/companies/webpack:/Users/flowers_ben/Projects/ortus/ortus-monorepo/node_modules/tsyringe/dist/esm5/dependency-container.js:140:1)
    at InternalDependencyContainer.../../../node_modules/tsyringe/dist/esm5/dependency-container.js.InternalDependencyContainer.resolve (/Users/flowers_ben/Projects/ortus/ortus-monorepo/packages/api/client/.webpack/service/src/handlers/companies/webpack:/Users/flowers_ben/Projects/ortus/ortus-monorepo/node_modules/tsyringe/dist/esm5/dependency-container.js:67:1)
    at Object../src/handlers/companies/listCompanyStages.ts (/Users/flowers_ben/Projects/ortus/ortus-monorepo/packages/api/client/.webpack/service/src/handlers/companies/webpack:/src/handlers/companies/listCompanyStages.ts:21:34)
    at __webpack_require__ (/Users/flowers_ben/Projects/ortus/ortus-monorepo/packages/api/client/.webpack/service/src/handlers/companies/webpack:/webpack/bootstrap:19:1)
    at /Users/flowers_ben/Projects/ortus/ortus-monorepo/packages/api/client/.webpack/service/src/handlers/companies/webpack:/webpack/bootstrap:83:1
    at Object.<anonymous> (/Users/flowers_ben/Projects/ortus/ortus-monorepo/packages/api/client/.webpack/service/src/handlers/companies/listCompanyStages.js:87:10)
    at Module._compile (internal/modules/cjs/loader.js:738:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:749:10)
    at Module.load (internal/modules/cjs/loader.js:630:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:570:12)
    at Function.Module._load (internal/modules/cjs/loader.js:562:3)
    at Module.require (internal/modules/cjs/loader.js:667:17)
    at require (internal/modules/cjs/helpers.js:20:18)
    at Object.createHandler (/Users/flowers_ben/Projects/ortus/ortus-monorepo/node_modules/serverless-offline/src/functionHelper.js:215:15)
    at handler (/Users/flowers_ben/Projects/ortus/ortus-monorepo/node_modules/serverless-offline/src/ApiGateway.js:485:40)
    at module.exports.internals.Manager.execute (/Users/flowers_ben/Projects/ortus/ortus-monorepo/node_modules/@hapi/hapi/lib/toolkit.js:41:33)
    at Object.internals.handler (/Users/flowers_ben/Projects/ortus/ortus-monorepo/node_modules/@hapi/hapi/lib/handler.js:46:48)
    at exports.execute (/Users/flowers_ben/Projects/ortus/ortus-monorepo/node_modules/@hapi/hapi/lib/handler.js:31:36)
    at Request._lifecycle (/Users/flowers_ben/Projects/ortus/ortus-monorepo/node_modules/@hapi/hapi/lib/request.js:312:68)
    at processTicksAndRejections (internal/process/next_tick.js:81:5)

Dependency on parameter values

Is is possible have parameter values as dependencies? Example:

import {singleton} from "tsyringe";

@singleton()
class Foo {
  private str: string;
  constructor(value: string) {
    this.str = value;
  }
}

// some other file
import "reflect-metadata";
import {container} from "tsyringe";
import {Foo} from "./foo";

const str = "test";

const instance = container.resolve(Foo); // Add "str" to "Foo" somehow here?

Plans for request scope (non-transient)?

Hello Tsyringe team,

I was wondering if there are any plans for supporting a 'request' scope. I'll give an example of what I mean:

@injectable()
class A {
  constructor(public b: B, public c: C) {}
}
class B {
  constructor(public x: X) {}
}
class C {
  constructor(public x: X) {}
}
@requestScope()
class X {}

const a = container.resolve(A);
a.b.x === a.c.x; // within a single dependency resolution inject the same instance instead of creating new ones. 

This can sometimes be very useful, for example if X sets up a database transaction that has to be used by anything within the container.resolve call, but not for subsequent calls to container.resolve.

Thanks!

Spring like @Configuration, @Bean

instead of calling .register to do multiple ... beans (for lack of a better term) that cannot be annotated with @Injectable due to ownership, or interface-ness, it would be nice to be able to have spring like

@Configuration
class MyConfig {
     @Bean("...") dbConnector(): PostgresDatabaseConnector {
      ...
     }
....
}

note: in spring MyConfig also becomes a registered "bean".

@singleton

Very nice and simple framework! Would you consider adding a @singleton shortcut decorator to mark such classes? This would allow to avoid the use of container register in many cases (with tests where mocks are used the most common exception).
Since there is a global container this should not be a problem. The docs do not mention singleton, but I see it is implemented.

Here a quick and dirty proof of concept, that works in the simple case:

import {container, decorators} from "tsyringe";

export const {injectable} = decorators;

export function singleton(constructor: any) {
    container.register(
        constructor,
        {useClass: constructor},
        {singleton: true}
    );

A suggestion about inheritance

The issue is that I have duplicate code when using inheritance

Here's a sample code from the parent class

@injectable()
export class DynamicEquation {

   public constructor (
    protected ajax: Ajax,
    protected tools: Tools,
    protected pubsub: PubSub,
    protected messenger: Messenger,
  ) {
  }

}

and then the child class

@injectable()
export class PriceEquation extends DynamicEquation {

  public constructor (
    protected ajax: Ajax,
    protected tools: Tools,
    protected pubsub: PubSub,
    protected messenger: Messenger,
  ) {
    super(ajax, tools, pubsub, messenger)
  }

}

A lot of the same code is repeated and I have to modify several files when I want to add a new dependency

Is there a way to remove the child constructor altogether and still have dependencies resolved?
Would it be a good behaviour to implement in tsyringe if not currently feasible?

injectAll is not accessible

Describe the bug
Although there is a decorator injectAll, that supports injecting multiple dependencies, as well as documentation for said decorator, it's currently impossible to use it. This decorator is simply not reexported in decorators/index.ts, which means that it doesn't get included into built package.

To Reproduce
install tsyringe package into any other package, explore node_modules/tsyringe, discover that built package contains no injectAll decorator

Expected behavior
import { injectAll } from 'tsyringe'; should work

Version:
3.3.0

Is is possible to resolve function parameters?

I want to do something like this

class Foo {}
class Bar {}
function fooBar(foo: Foo, bar: Bar){
    return () => {
       // can use foo and bar instances
    }
}
const fb = container.resolve(fooBar)

I can do this, but I don't like [Foo, Bar] part.

function resolveFunction<T extends any[], R>(fn: (...x: T) => R,
  deps: {[K in keyof T]: InjectionToken<T[K]>}): R {
    const r = deps.map(container.resolve)
    return fn(...(r as any))
}

const x = resolveFunction((foo: Foo, bar: Bar) => ..., [Foo, Bar])

Is is possible to do something better?

Issue registering singleton

I believe there is an issue with the container.registerSingleton method.

Suppose I have a class that depends on a singleton service as follows

@singleton()
class MyService { ... }

class MyClass {
  constructor(private myService: MyService) { }
}

Then suppose I wish to mock this service in a test.

container.registerSingleton(MyService, MyServiceMock);
const myClass = container.resolve(MyClass);

This fails as MyClass is instantiated with MyService rather than MyServiceMock. I believe the issue is at the bottom of the registerSingleton method shown here for reference:
https://github.com/Microsoft/tsyringe/blob/a1df21283d4e19ef7006848a800a6e9caa524c9b/src/dependency-container.ts#L70-L88

I believe lines 85-87 are the issue. useClass: from should be useClass: to. Or am I misusing this method?

README examples do not match API

I don't believe the examples in the README match the current API. For example:

container.register({
  token: "SuperService",
  useClass: TestService
});

From what I can tell there is no register method that only takes one argument.

documentation , @registry is confusing

You can also mark up any class with the @registry() decorator to have the given providers registered upon importing the marked up class. @registry() takes an array of providers like so:

@injectable()
@registry([
  Foo,
  Bar,
  {
    token: "IFoobar",
   useClass: MockFoobar
   }
 ])
 class MyClass {}

I tried this

@registry([ ApolloConfig ])
export default class RegistryConfigurer {
...
export default class ApolloConfig {

but it didn't like that syntax... compiletime errors, this however works

@registry([
  { token: 'apolloConfig', useClass: ApolloConfig },
])

I suspect the issue is just my understanding of the docs, but perhaps they could be elaborated upon to be clearer.

support PropertySource

another spring/boot feature that would be nice, is "property sources", what a property source is, is a location of "properties" like foo.bar=baz I imagine this wouldn't be exactly the same for typescript, though it could be.. I imagine the source of this would be either json, or an .env, though spring core itself isn't prescriptive and only supports a few things out of the box.

{
    "foo": {
       "bar": "baz"
     },
    "my-bool": true,
}

or you could override this with a env

FOO_BAR=baz
MYBOOL=true

and if both existed the env var would override the json file.

then you could inject it as so (spring also supports type conversion)

@injectable()
class MyService {
    private _myBool: boolean = false;

    constructor ( @value('foo.bar') private fooBar: String ) {
    }
    get myBool(): boolean {
       return this._myBool;
    }
    @inject() // some marker required to know to call setter? uncertain about this...
    set myBool(@value('my-bool') value: boolean) {
        this._myBool = value;
    }
}

you'd need some sort of collection in the registry, probably something like

// this may also exist by default with the above suggested
container.register( "propertySources", { // probably a special key, possibly a special type
     propertySources: {
         env: new EnvPropertySource,
         json: new JsonPropertySource('myconfig.json')
     }
}

reference for how to use the feature

https://www.baeldung.com/properties-with-spring

and upstream documentation

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/PropertySource.html
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/env/PropertySource.html
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config

I'm really not hoping for the prescriptiveness of spring boot, but maybe just simply the ability to define a collection of property sources and inject values (note: property source values are not in springs container)

probably worth noting spring boot also has this feature https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config-typesafe-configuration-properties

@ConfigurationProperties("foo") // foo is the property prefix, thus my-bool wouldn't go in here
class BarProperties {
    bar: String// no need for value, uses setter or constructor injection
}

resolveAll documentation

How can I use resolveAll to pull in more than one instance of an interface/abstract class that were defined as @singleton? I've tried a couple of iterations, however I keep getting "token not registered" or some other error. I looked at the tests and noticed it's only resolving a single instance of IFoo

Throw instances of Error and not plain strings.

Describe the bug
In execution time if any problem is encountered during dependencies resolution the throw argument by tsyringe is a plain string. A string miss critical information for debugging (a proper stack trace).

To Reproduce
Hit any container resolution error. Catch exception looks like:

pry/node_modules/tsyringe/dist/cjs/dependency-container.js:175
            throw `TypeInfo not known for ${ctor}`;
// no more info

Expected behavior
Throw a proper error instance (as throw new Error("TypeInfo not known for ${ctor}")) and the dumped catch error will be:

Error: TypeInfo not known for function Object() { [native code] }
    at InternalDependencyContainer.construct (/var/www/packages/lechuza/node_modules/tsyringe/dist/cjs/dependency-container.js:175:19)
    at InternalDependencyContainer.resolve (/var/www/packages/lechuza/node_modules/tsyringe/dist/cjs/dependency-container.js:78:21)
    at /var/www/packages/lechuza/node_modules/tsyringe/dist/cjs/dependency-container.js:183:25
    at Array.map (<anonymous>)
    at InternalDependencyContainer.construct (/var/www/packages/lechuza/node_modules/tsyringe/dist/cjs/dependency-container.js:177:34)
    at InternalDependencyContainer.resolveRegistration (/var/www/packages/lechuza/node_modules/tsyringe/dist/cjs/dependency-container.js:102:24)
    at InternalDependencyContainer.resolve (/var/www/packages/lechuza/node_modules/tsyringe/dist/cjs/dependency-container.js:76:25)
    at /var/www/packages/lechuza/src/LechuGameServer.ts:72:37
    at Object.callbackOrEmit [as callback_or_emit] (/var/www/packages/lechuza/node_modules/redis/lib/utils.js:89:9)
    at RedisClient.return_error (/var/www/packages/lechuza/node_modules/redis/index.js:706:11)
    at JavascriptRedisParser.returnError (/var/www/packages/lechuza/node_modules/redis/index.js:196:18)
    at JavascriptRedisParser.execute (/var/www/packages/lechuza/node_modules/redis-parser/lib/parser.js:572:12)
    at Socket.<anonymous> (/var/www/packages/lechuza/node_modules/redis/index.js:274:27)
....

Stack trace is critical to trace origin of error.

Version:
4.0.1

isRegistered not detecting tokens from registry

title might be wrong, here's what I'm doing

@singleton()
export default class StripeConnectionTokenResolversFactory extends AbstractResolversFactory {
@registry([
  { token: InjectToken.GraphFieldResolvers, useToken: StripeConnectionTokenResolversFactory },
  { token: InjectToken.ApolloProvider, useClass: ApolloProvider },
  { token: InjectToken.ContentfulProvider, useClass: ContentfulProvider },
  { token: InjectToken.StripeProvider, useClass: StripeProvider },
])
export default class BootStrapper {

now this code elsewhere does not work, I get the empty array

  const graphResolvers = resolver().isRegistered(InjectToken.GraphFieldResolvers)
    ? resolver().resolveAll<ResolversFactory>(InjectToken.GraphFieldResolvers)
      .flatMap((o) => o.resolvers())
    : [];

this however does work, and I get the one resolver, and the removal of the ternary is the only thing I've changed.

  const graphResolvers = resolver().resolveAll<ResolversFactory>(InjectToken.GraphFieldResolvers)
      .flatMap((o) => o.resolvers());

note: resolver() just returns my deepest child container...

const test = container.createChildContainer();
const mock = test.createChildContainer();
// must use the most "childish" container or it won't work
const resolverFun = util.deprecate(() => mock, 'calling resolve is an antipattern');

export function rootContainer(): DependencyContainer {
  return container;
}

export function testContainer(): DependencyContainer {
  return test;
}

export function mockContainer(): DependencyContainer {
  return mock;
}

export function resolver(): DependencyContainer {
  return resolverFun();
}

Version: 4.0.1

How to use with other decorators?

function logNew() {
  return <T extends constructor<any>>(constructor: T) => {
    return class extends constructor {
      constructor(...args: any[]) {
        super(...args);
        console.log("CREATED", args)
      }
    }
  };
}

class Foo {
  value = "foo"
}

@logNew()
@injectable()
class Bar {
  constructor(foo: Foo){
    console.log(foo.value)
  }
}

const bar = container.resolve(Bar)

foo is undefined

Better error message in case of Circular Dependency

When a circular dependency occur, the error message is TypeError: Cannot read property 'length' of undefined, which is really misleading.

// file b.ts
@singleton()
export class B {
  constructor(private other: A) {}
}

// file b.ts
@singleton()
export class A {
  constructor(private other: B) {}
}

// usage
container.resolve(A) // TypeError: Cannot read property 'length' of undefined

That occur here in the source when resolving the B class :

class DependencyContainer {
  // ...
  construct(ctor) {
    if (ctor.length === 0) { // error here, ctor is undefined
      return new ctor();
    }
    // ...
  }
}

It would be great to have a better error message like

construct(ctor) {
  if (ctor == null) {
    throw "Cannot read class constructor, maybe there is a circular dependency"
  }
  if (ctor.length === 0) {
    return new ctor();
  }
  // ...
}

This error also occur with a bad ordering definitions in the same file :

@singleton()
class MyClass {
  constructor(private other: Dummy) {}
}

class Dummy {} // if this class is defined above it works :)

container.resolve(MyClass) // TypeError: Cannot read property 'length' of undefined

I don't know if there is other cases in which ctor is undefined, let this to you :)

How about `InjectionToken` type add `symbol` support?

In providers.ts, add symbol support.

export type InjectionToken<T = any> = constructor<T> | string | symbol;

For fast completion input by IDE.

const TOKENS = {
  Foo: Symbol(),
  Bar: Symbol(),
  //...
};

interface Foo {
}

class FooImpl implements Foo {
}

@injectable()
class Bar {
   constructor(@inject(TOKENS.Foo) foo: Foo) {}
}

container.register(TOKENS.Foo, FooImpl)
container.resolve<Bar>(Bar);

Check peer dependencies for a reflect polyfill upon install

This package has an implicit peer dependency on a reflect polyfill, we should check to see if one is installed in the postinstall. If not we could output a message like:

[WARN] tsyringe requires a reflect polyfill, but none is installed. Consider installing one of "reflect-metadata", "core-js", or "reflection".

Class properties are lost when decorating on Node 13.X

Describe the bug
When using Node 13, accessing the constructor property of the class is hidden by the decorators of tsyringe.

To Reproduce

import "reflect-metadata";

import { autoInjectable } from "tsyringe";

abstract class Service {
  serviceName(): string {
    return this.constructor.name;
  }
}

function printClass<T extends Service>(): (target: any) => void {
  return function(target: any): void {
    console.log("Decorator for: ", target.name);
  };
}

@printClass()
@autoInjectable()
class AutoInjectableClass extends Service {}
console.log("AutoInjectable instance name: ", new AutoInjectableClass().serviceName());

@printClass()
class RegularClass extends Service {}
console.log("Regular instance name: ", new RegularClass().serviceName());

Node 13.X:

Decorator for:  
AutoInjectable instance name:  
Decorator for:  RegularClass
Regular instance name:  RegularClass

Expected behavior
Node 12.X:

Decorator for:  AutoInjectableClass
AutoInjectable instance name:  AutoInjectableClass
Decorator for:  RegularClass
Regular instance name:  RegularClass

Version:
Node > 13.0
Tsyringe 3.7.0

Child container doesn't auto inject from same container for ContainerScoped classes

Describe the bug
When using a child container, when resolving a class (with Lifecycle.ContainerScoped) its auto injected classes (also Lifecycle.ContainerScoped) are not taken from the child container.

To Reproduce

import { autoInjectable, container, Lifecycle, scoped } from "tsyringe";

@scoped(Lifecycle.ContainerScoped)
export class A {}

@scoped(Lifecycle.ContainerScoped)
@autoInjectable()
export class B {
  constructor(public a: A) {}
}

const childContainer = container.createChildContainer();
const a = childContainer.resolve(A);
const b = childContainer.resolve(B);

console.log(a === b.a); // returns false, expecting true

Expected behavior
the auto injected class should be resolved from the same container

Version: 4.0.1

Abstract class constructor type issue

I tried using the injectable decorator with an abstract class but it gave me this error

Argument of type 'typeof MyClass' is not assignable to parameter of type 'constructor<{}>'.
      Cannot assign an abstract constructor type to a non-abstract constructor type.

Sample code

import { injectable } from "tsyringe";

@injectable()
abstract class MyClass {

}

I modified the declaration just to test (based on https://stackoverflow.com/questions/36886082/abstract-constructor-type-in-typescript)

type abstract_ctor<T> = Function & { prototype: T };
export declare function injectable<T>(): (target: constructor<T>|abstract_ctor<T>) => void;

and the compilation worked without errors

I will try cloning and running some tests to see if DI works fine with an abstract class

How to resolve dependencies based on type?

If I have two classes:

class Foo (
    contructor(@inject("IValidator") private validator: IValidator<Foo>) {}
)

class Bar (
    contructor(@inject("IValidator") private validator: IValidator<Bar>) {}
)

Can I have two separate instances of validator in this situation? Could I have a BarValidator class and a FooValidator class?

Vanilla Javascript examples

A lightweight dependency injection container for TypeScript/JavaScript for constructor injection.

But I only see typescript examples and no javascript only example. Is it even possible to use it without typescript?

special handling of asyncs and promises

so I have this code

export function context(): DependencyContainer {
  container.register(
    Beans.APOLLO_CONFIG,
    {
      useFactory: instanceCachingFactory<Promise<Config>>(async(c) => {
        return getApolloServerConfig();
      }),
    });
  return container;
}

getApolloServerConfig is an async function

now all the way down I have to do this

export function testContext(): DependencyContainer {
  const testContainer = context().createChildContainer();
  testContainer.register(
    TestBeans.APOLLO_SERVER,
    {
      useFactory: instanceCachingFactory<Promise<ApolloServer>>(async(c) => {
        const config = await c.resolve<Promise<Config>>(Beans.APOLLO_CONFIG);
        return new ApolloServer({
          ...config,
          engine: false,
        });
      }),
    });

  testContainer.register(
    TestBeans.APOLLO_TEST_CLIENT,
    {
      useFactory: instanceCachingFactory<Promise<ApolloServerTestClient>>(async(c) => {
        const server = await c.resolve<Promise<ApolloServer>>(TestBeans.APOLLO_SERVER);
        return createTestClient(server);
      }),
    });
  return testContainer;
}

including a final await in after my resolve.

what I'd really like to do (and maybe this isn't possible? is to return the promise on the first one, but have the following calls simply request the result of the first promise.

export function testContext(): DependencyContainer {
  const testContainer = context().createChildContainer();
  testContainer.register(
    TestBeans.APOLLO_SERVER,
    {
      useFactory: instanceCachingFactory<Promise<ApolloServer>>((c) => {
        const config = await c.resolve<Config>(Beans.APOLLO_CONFIG);
        return new ApolloServer({
          ...config,
          engine: false,
        });
      }),
    });

  testContainer.register(
    TestBeans.APOLLO_TEST_CLIENT,
    {
      useFactory: instanceCachingFactory<ApolloServerTestClient>((c) => {
        const server = await c.resolve<ApolloServer>(TestBeans.APOLLO_SERVER);
        return createTestClient(server);
      }),
    });
  return testContainer;
}

this may not be possible in any sort of sane rational way, I'm uncertain as I'm only recently coming to this from a Java/Spring world.

How to handle circular dependecy

So let's say I have first class TestA that has dependency to class TestB

// TestA.ts
import { inject, injectable } from "tsyringe";
import { ITestB } from "./TestB";

export interface ITestA {
    helloA(): void;
    waveToB(): void;
}

@injectable()
export class TestA implements ITestA {
    constructor(@inject("ITestB") private testB: ITestB) { }
    /**
     * helloA
     */
    public helloA(): void {
        console.log("Hello from class A");
    }
    public waveToB(): void {
        console.log("waving to B");
        this.testB.helloB();
    }
}

and then I have second class TestB that has dependency to TestA:

// TestB.ts
import { inject, injectable } from "tsyringe";
import { ITestA } from "./TestA";

export interface ITestB {
    helloB(): void;
}

@injectable()
export class TestB implements ITestB {
    constructor(@inject("ITestA") private testA: ITestA) { }
    /**
     * helloB
     */
    public helloB(): void {
        console.log("Hello from class B");
    }
    public waveToA(): void {
        console.log("waving to A");
        this.testA.helloA();
    }
}

And finally main:

import { container } from "tsyringe";
import { TestA } from "./lib/TestA";
import { TestB } from "./lib/TestB";
const testA = container.resolve(TestA);
testA.helloA();
testA.waveToB();
const testB = container.resolve(TestB);
testB.helloB();

Which upon running results in "Maximum call stack size exceeded" and crashes. Is there any solution to this without restructuring the code? (sometimes it is not that simple).
I've seen probable solution by @unlocomqx : #20 but I'm unable to compile it due to errors.

@autoInjectable() and @singleton() can't use together.

I have one class like this.

@autoInjectable()
@singleton()
export class AppEventEmitterService extends EventEmitter {
    private readonly logger: Logger

    constructor(private loggerService?: LoggerService) {
...

I created like this because I need to log on each event that emit and this class will using across of app. Problem that I found is this class will don't be singleton if I add autoInjectable. Right now I need to create logger as const variable outside of class and using from that.

Just ask at here if someone have same problem like me.

Inject an empty array with @injectAll() when nothing registered for the token

Is your feature request related to a problem? Please describe.
I'm using @injectAll('feature') to inject an array of Features into a Server. I'd like to unit test some of the mechanisms of the Server class but the resolve fails when zero feature has been registered on the container.

Description
Would it make more sense to inject an empty array with @injectAll() when no service has been registered on the container and the token is unknown by the container?

Alternate solutions
I tried @injectAll('feature') private readonly features?: Feature[] or @injectAll('feature') private readonly features: Feature[] = []but same error.

Additional context
By looking (quickly) at the code, it seems to be a one-line change at https://github.com/microsoft/tsyringe/blob/master/src/dependency-container.ts#L242 (apart from the tests) but maybe it breaks things or maybe it's not how it should behave.
I can try submitting a PR if that's OK with you.

How would I use tsyringe with express?

I've set up my express project so that I have controllers, services, and repositories.

The following works:

// bootstrap.ts
import 'reflect-metadata';
import { container } from 'tsyringe';

import { AuthService, UserService } from './services';
import { UserRepository } from './repositories';
import { AuthController } from './controllers';

container.register('IUserRepository', {
	useClass: UserRepository
});

container.register('IUserService', {
	useClass: UserService
});

container.register('IAuthService', {
	useClass: AuthService
});

export const authController = container.resolve(AuthController);

Here AuthController depends on IUserService and IAuthService, and IUserService depends on IUserRepository.

The problem I am having is when I call a method of authController like so:

import * as express from 'express';

import { authController } from './bootstrap'

const router = express.Router();

router.get('test', authController.test);

If I hit the server's 'test' route, authController.test does get fired, however if inside authController.test a dependency is being used the application hangs.

I can use dependencies right after I resolve authController in the bootstrap file, but after the server is started, and when I try to hook up express routes with authController methods the dependencies are "gone".

TLDR;
How do I use tsyringe with express where routes fire methods from an @injectable() class?

Injecting third party Class (Singleton scope)

Hi,

I have been experimenting with this library and I'm trying to resolve an instance of ApolloClient which I registered as an instance via registerInstance. The instance uses a configuration that I manually inject via container.resolve, like so:

container.registerInstance(
    "ApolloClient",
    new ApolloClient(container.resolve(ApolloClientConfiguration))
);

ApolloClientConfiguration is my own class which has an @injectable decorator. The issue I'm having is that Tsyringe throws Error: TypeInfo not known for function ApolloClient(options) { .... Is it possible to register and inject third party Classes with Tsyringe?

I resolve the instance with

const apolloClient = container.resolve<ApolloClient<NormalizedCacheObject>>(
    "ApolloClient"
);

Thanks

npm package v3.3.0 missing resolveAll method in DependencyContainer interface

Describe the bug
The documentation suggests v3.3.0 has resolveAll implemented but its not in the published package.

To Reproduce

  1. npm install tsyringe, notice package.json is v3.3.0
  2. notice dist\typings\types\dependency-container.d.ts is missing resolveAll method
  3. notice also missing from all dependency-container.js files

Expected behavior
it should be there

Version:
3.3.0

Not working with default values

Please correct me if I'm wrong, but it doesn't seem like tsyringe is working with default constructor values? It would be nice if the default values would be used if no value for the input is provided. Example:

import 'reflect-metadata';
import { container } from 'tsyringe';

class TestClass {
  constructor(private str: string = 'string') {}
}

let instance = container.resolve<TestClass>(TestClass);

Gives the following error:

    TypeInfo not known for function TestClass(str) {

              if (str === void 0) { str = 'string'; }
              this.str = str;
          }

Preferably this would run and the str variable would be set to the default value of "string".

Typed array resolution should not be allowed

Describe the bug

Currently, you can register any array as an array of a given type. This isn't a huge problem as all arrays are registered as Array, but that in and of itself is confusing.

To Reproduce

Simply register one type of array with another type of array in the type param.

const bazArray = [new Baz()];
globalContainer.register<Foo[]>(Array, {useValue: bazArray});

Expected behavior
Attempting to register an array like this should result in an error

Testing singletons

I'm writing tests against a class marked @singleton(). In my beforeEach I resolve the class, but since it's a singleton I get the same instance for each test. Ideally each test would be independent of the others. I tried calling container.reset() but that wipes out all of the registered types. Is there room in this library for a "soft" reset, or is there another approach I could take?

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.