Confused about when to use `.inSingletonScope()` and `.toService()`

I’m still pretty new to TypeScript and Inversify. I think I have a general understanding of Inversify, but I’m confused about when I need to use .inSingletonScope() or .toService() when configuring dependencies.

My understanding is that singleton means it will create “one and only one” instance, otherwise it will create new instances for each requested dependency. When I look at some example code, CommandContribution is sometimes configured in singleton scope, while other times it’s configured using .toService() without .inSingletonScope(). I don’t see why one would choose one vs the other.

Is there a general rule to follow for when to use each of these methods?

1 Like

Your understanding of the singleton scope is correct. toService is for a transitive binding of an identifier to another identifier. Not a binding from an identifier to an implementation. I hope it is clear that defining scope for transitive binding is meaningless. The scope will be derived from actual implementation of referenced identifier.

2 Likes

@akosyakov Thanks for the explanation, I’m still trying to wrap my head around this.

In the “How to inversify in Eclipse Theia” article, it shows how to do a normal binding from an interface to an implementation:

bind(InterfaceA).to(ClassA);

Then it says in order to bind multiple interfaces to one implementation, you can do this:

[InterfaceB, InterfaceC].forEach(i => bind(i).toService(ClassBC));

This confuses me because it doesn’t look like a transitive binding – it’s still binding interface to implementation, the only difference is that we’re binding two interfaces to one implementation. Why is toService() necessary in this example as opposed to to()?

Or, am I misinterpreting something?

bind(ClassBC).toSelf().inSingletonScope();
[InterfaceB, InterfaceC].forEach(i => bind(i).toService(ClassBC));

The single liner might have been missing some context, but this should help.

[InterfaceB, InterfaceC].forEach(i => bind(i).to(ClassBC).inSingletonScope());

The above snippet will actually instantiate ClassBC twice: once for each interface.

Using toService referring to a unique binding prevents that.

Oh, I see, that makes more sense now. So we first bind toSelf() to get a resolution from the ClassBC identifier to the ClassBC implementation, and then we use toService() to resolve both InterfaceA and InterfaceB to the ClassBC identifier (which then resolves to the implementation b/c of the toSelf() binding). Is that right?

Yes.

1 Like

@akosyakov how will you explain transitive binding to 10yrs old (or maybe intern)?.

Your understanding of the singleton scope is correct. toService is for a transitive binding of an identifier to another identifier. Not a binding from an identifier to an implementation. I hope it is clear that defining scope for transitive binding is meaningless. The scope will be derived from actual implementation of referenced identifier.

Can you explain this with an example?

You can check the docs from InversifyJS. You can also glance into the feature request.


Update:

I would explain it as transitivity or transitive relationship in general.

When a mammal is an animal, and a cat is a mammal, it implies a cat is an animal. You do not have to declare the last statement explicitly.

More formally: whenever x > y and y > z , thenx also x > z.

2 Likes