Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] Allow registering multiple providers for the same token #1693

Closed
derevnjuk opened this issue Dec 28, 2021 · 13 comments
Closed

[FEAT] Allow registering multiple providers for the same token #1693

derevnjuk opened this issue Dec 28, 2021 · 13 comments

Comments

@derevnjuk
Copy link
Contributor

derevnjuk commented Dec 28, 2021

Provide the ability to resolve all instances registered against a given token with injector.getAll().

Goals

This feature lets to simplify the dependency management while working on multiple implementations of the same interface with type code.

Informations

If users use the same token when registering providers, the IoC container should exchange a token for a list of instances. Let's consider the following real example:

interface Bar {
  type: string;
}

const Bar: unique symbol = Symbol("Bar")

@Injectable()
class Foo implements Bar {
  private readonly type = 'foo';
}

@Injectable()
class Baz implements Bar {
  private readonly type = 'baz';
}

Now as a user, I would like to create a registry and retrieve an appropriate instance by type:

@Controller('/some')
export class SomeController {
  constructor(@Inject(Bar) private readonly bars: Bar[]) {}

  @Post()
  async create(@Body('type') type: 'baz' | 'foo') {
    const bar: Bar | undefined = this.bars.find((x) => x.type === type);

    // your code
  }
}

or in the following way as well:

@Controller('/some')
export class SomeController {
  constructor(private readonly injector: InjectorService) {}

  @Post()
  async create(@Body('type') type: 'baz' | 'foo') {
    const bars: Bar[] = this.injector.getAll<Bar>(Bar);
    const bar: Bar | undefined = bars.find((x) => x.type === type);

    // your code
  }
}

In both cases, it will inject an array using the passed token to resolve the registered instances.

@derevnjuk derevnjuk changed the title Allow registering multiple providers for the same token [FEAT] Allow registering multiple providers for the same token Dec 28, 2021
@Romakita
Copy link
Collaborator

Romakita commented Dec 28, 2021

Hello @derevnjuk
Thanks for this issue. Yes, I see the principle, inversifyjs have this feature but I haven’t implemented it because it’s a bit complex. Only one token can be associated to an instance. Change that can be a huge rework.

Actually, the nerest existing feature in Ts.ED is the provider.type / injector.getProviders(type):

@Injectable({type: 'Bar'})
class Foo implements Bar {
  private readonly type = 'foo';
}

// injector class
 getAll<T>(type:string): T[] { 
  return this.getProviders(type).map(provider => this.get(povider.token))
}

I’m surprised to not have already implemented this method…

If this solution is ok, we can improve the inject decorator to inject providers by type if the typing metadata is an array.

Note: type accept only a string, but it’s ok for to change the typings to accept a symbol.

See you ;)
Romain

@stale
Copy link

stale bot commented Feb 27, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Feb 27, 2022
@Romakita Romakita added pinned and removed wontfix labels Feb 27, 2022
@borjapazr
Copy link
Contributor

Hello @Romakita ! First of all thank you for this great work! 🙏

Implementing this functionality would be a wonderful and useful thing. What is the status?

Thanks 😊

@Romakita
Copy link
Collaborator

Hello @borjapazr

thanks for your message.

I’m sorry but it’s not on my roadmap. But already given a solution to have similar things.

The DI design is based on one Token = one instance.

Changing that will be complicated and will probably have an effect one the performance (server bootstrap), for a limited usage.

Actually, I never had this need in my professional project. This is why, It’s a bit complicated for me to understand this needs. I understand that you want it. But I haven’t a real scenario where this feature while be a benefits.

Maybe I’m wrong. I’m open to be convinced and maybe, the given workaround can be a good alternative (with a decorator to symplify that)?

@borjapazr
Copy link
Contributor

Hello @Romakita

I understand your position and it seems reasonable to me. As I was reviewing, the solution you proposed can be useful in many occasions where this functionality is required. Also, if it can be improved with a decorator it would be great!

Thanks again 🙏

@Romakita
Copy link
Collaborator

Updated example for implementation:

This feature lets to simplify the dependency management while working on multiple implementations of the same interface with type code.

If users use the same token when registering providers, the IoC container should exchange a token for a list of instances. Let's consider the following real example:

interface Bar {
  type: string;
}

const Bar: unique symbol = Symbol("Bar")

@Injectable({type: Bar})
class Foo implements Bar {
  private readonly type = 'foo';
}

@Injectable({type: Bar})
class Baz implements Bar {
  private readonly type = 'baz';
}

Now as a user, I would like to create a registry and retrieve an appropriate instance by type:

@Controller("/some")
export class SomeController {
  constructor(@Inject(Bar) private readonly bars: Bar[]) {}

  @Post()
  async create(@Body("type") type: "baz" | "foo") {
    const bar: Bar | undefined = this.bars.find((x) => x.type === type);
  }
}

@Romakita
Copy link
Collaborator

Work in progress ;)

@github-actions
Copy link

🎉 Are you happy?

If you appreciated the support, know that it is free and is carried out on personal time ;)

A support, even a little bit makes a difference for me and continues to bring you answers!

github opencollective

@Romakita
Copy link
Collaborator

🎉 This issue has been resolved in version 6.129.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@Romakita
Copy link
Collaborator

🎉 This issue has been resolved in version 7.0.0-rc.5 🎉

The release is available on:

Your semantic-release bot 📦🚀

@Romakita
Copy link
Collaborator

@derevnjuk @borjapazr the feature is released. You can the documentation here

@Romakita
Copy link
Collaborator

🎉 This issue has been resolved in version 7.0.0-rc.6 🎉

The release is available on:

Your semantic-release bot 📦🚀

@Romakita Romakita moved this to Done in Global Board Oct 3, 2022
@Romakita
Copy link
Collaborator

Romakita commented Oct 8, 2022

🎉 This issue has been resolved in version 7.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

No branches or pull requests

3 participants