inversify / InversifyJS

A powerful and lightweight inversion of control container for JavaScript & Node.js apps powered by TypeScript.
http://inversify.io/
MIT License
11.21k stars 715 forks source link

Add .toDynamicValueWithDeps into BindingToSyntax #1506

Open SamakaCD opened 1 year ago

SamakaCD commented 1 year ago

toDynamicValueWithDeps method binds an abstraction to a dynamic value with required dependencies from the container in a declarative way.

Description

I've addded toDynamicValueWithDeps method into BindingToSyntax class which basically uses toDynamicValue method and context it provides under the hood of the new method.

Related Issue

None.

Motivation and Context

Let's say we have the following classes:

abstract class AbstractShuriken {}

abstract class AbstractKatana {}

@injectable()
class Shuriken implements AbstractShuriken {}

@injectable()
class Katana implements AbstractKatana {}

class Ninja {
  public constructor(public shuriken: AbstractShuriken, public katana: AbstractKatana) {}
}

I would like to create a binding for Ninja class which depends on abstract shuriken and katana. Without the new method we would do something like this:

container.bind(Ninja).toDynamicValue((context) => {
  const shuriken = context.container.get(AbstractShuriken)
  const katana = context.container.get(AbstractKatana)
  return new Ninja(shuriken, katana)
})

The code might be even longer, especially when it's required to inject more dependencies. To make binding notation shorter we could introduce a method which accepts a list of dependencies and a factory which toDynamicValueWithDeps method actually does:

container.bind(Ninja).toDynamicValueWithDeps(
  [AbstractShuriken, AbstractKatana] as const,
  ([shuriken, katana]) => new Ninja(shuriken, katana)
)

Looking forward, it should be possible to remove as const for dependencies using const type parameters when the library will use TypeScript 5.

How Has This Been Tested?

I've added should be able to resolve all dependencies using toDynamicValueWithDeps test case into container.test.ts.

Types of changes

Checklist:

PodaruDragos commented 1 year ago

This add a new method to the binding, so I am a bit reticent about this. code looks good though. Might need more opinions on this.

PS: you could always just build this on your own project, TBH

SamakaCD commented 1 year ago

you could always just build this on your own project, TBH

And that's right šŸ™‚. Initially, I tried to extend ContainerModule class to replace the original binding syntax. But the result was somewhat cumbersome šŸ˜…

Anyway, it is up to you whether to include such a binding extension.

SamakaCD commented 1 year ago

Improved generic constraints for toDynamicValueWithDeps. Also, updated the "Motivation and Context" section to highlight that it's not required to decorate binding class / service identifier with @injectable().