diff --git a/CHANGELOG.md b/CHANGELOG.md index ba30949c6..058cd9c1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- Added `.toDynamicValueWithDeps` to create a dynamic value with declaratively listed dependencies ### Changed diff --git a/src/interfaces/interfaces.ts b/src/interfaces/interfaces.ts index 773833c1d..b73082cec 100644 --- a/src/interfaces/interfaces.ts +++ b/src/interfaces/interfaces.ts @@ -340,6 +340,13 @@ namespace interfaces { toSelf(): BindingInWhenOnSyntax; toConstantValue(value: T): BindingWhenOnSyntax; toDynamicValue(func: DynamicValue): BindingInWhenOnSyntax; + toDynamicValueWithDeps( + dependencies: Deps, + func: ( + dependencies: ResolvedDeps, + context: interfaces.Context + ) => T + ): interfaces.BindingInWhenOnSyntax; toConstructor(constructor: Newable): BindingWhenOnSyntax; toFactory( factory: FactoryCreator): BindingWhenOnSyntax; @@ -369,6 +376,17 @@ namespace interfaces { userGeneratedMetadata: MetadataMap; } + export type ResolvedDeps = { + [P in keyof Deps]: Deps[P] extends string + ? unknown + : Deps[P] extends symbol + ? unknown + : Deps[P] extends interfaces.Newable + ? R1 + : Deps[P] extends interfaces.Abstract + ? R2 + : unknown; + }; } export { interfaces }; diff --git a/src/syntax/binding_to_syntax.ts b/src/syntax/binding_to_syntax.ts index cfcf9ca95..b158820b8 100644 --- a/src/syntax/binding_to_syntax.ts +++ b/src/syntax/binding_to_syntax.ts @@ -46,6 +46,18 @@ class BindingToSyntax implements interfaces.BindingToSyntax { return new BindingInWhenOnSyntax(this._binding); } + public toDynamicValueWithDeps( + deps: Deps, + func: (dependencies: interfaces.ResolvedDeps, context: interfaces.Context) => T + ): interfaces.BindingInWhenOnSyntax { + return this.toDynamicValue((context) => { + const resolvedDeps = deps.map((identifier) => + context.container.get(identifier) + ) as unknown as interfaces.ResolvedDeps; + return func(resolvedDeps, context) + }) + } + public toConstructor(constructor: interfaces.Newable): interfaces.BindingWhenOnSyntax { this._binding.type = BindingTypeEnum.Constructor; this._binding.implementationType = constructor as unknown as T; diff --git a/test/container/container.test.ts b/test/container/container.test.ts index 6b0f9b3d1..3d81e1ceb 100644 --- a/test/container/container.test.ts +++ b/test/container/container.test.ts @@ -1172,4 +1172,32 @@ describe('Container', () => { expect(() => myContainer.resolve(CompositionRoot)).not.to.throw; }) + it('should be able to resolve all dependencies using toDynamicValueWithDeps', () => { + 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) {} + } + + const container = new Container() + container.bind(AbstractShuriken).to(Shuriken) + container.bind(AbstractKatana).to(Katana) + container.bind(Ninja).toDynamicValueWithDeps( + [AbstractShuriken, AbstractKatana] as const, + ([shuriken, katana]) => new Ninja(shuriken, katana) + ) + + const ninja = container.get(Ninja) + expect(ninja.shuriken).to.be.instanceOf(Shuriken) + expect(ninja.katana).to.be.instanceOf(Katana) + }) + }); diff --git a/wiki/value_injection.md b/wiki/value_injection.md index 3e8b8c12a..575a22389 100644 --- a/wiki/value_injection.md +++ b/wiki/value_injection.md @@ -9,3 +9,13 @@ container.bind("Katana").toDynamicValue((context: interfaces.Context) => // a dynamic value can return a promise that will resolve to the value container.bind("Katana").toDynamicValue((context: interfaces.Context) => { return Promise.resolve(new Katana()); }); ``` + +Binds an abstraction to a dynamic value with required dependencies from the container in a declarative way. +```ts +container.bind(AbstractShuriken).to(Shuriken) +container.bind(AbstractKatana).to(Katana) +container.bind(Ninja).toDynamicValueWithDeps( + [AbstractShuriken, AbstractKatana] as const, + ([shuriken, katana]) => new Ninja(shuriken, katana) +) +```