microsoft / tsyringe

Lightweight dependency injection container for JavaScript/TypeScript
MIT License
5.16k stars 173 forks source link

isRegistered not detecting tokens from registry #79

Open xenoterracide opened 4 years ago

xenoterracide commented 4 years ago

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

Xapphire13 commented 4 years ago

Can you write a simple unit test that can repro this?

xenoterracide commented 4 years ago

tried writing this unit test as a PR, but for some reason this doesn't compile in project... this reproduces, the child container is necessary.

import { container, injectable, registry } from 'tsyringe';

test('isRegistered', () => {
  interface MyType {
  }

  @injectable()
  class Foo implements MyType {
  }

  @injectable()
  class Bar implements MyType {
  }

  @registry([
    { token: 'MyType', useToken: Foo },
    { token: 'MyType', useToken: Bar },
  ])
  class Config {
  }

  const child = container.createChildContainer();

  expect(child.resolveAll<MyType>('MyType')).toHaveLength(2);
  expect(child.isRegistered('MyType')).toBe(true);
});

it is possible, even probable that this is the expected behavior on a child, but from what I can tell this leaves us without a good way to check if a collection is resolvable at all.

skiptirengu commented 4 years ago

You should pass true as the second parameter for the isRegistered method to allow a recursive search on parent containers.

child.isRegistered('MyType', true)