Skip to content

Commit 801a82d

Browse files
committed
fix(context): fix optional param injection for methods
The following function has length of 0 as the param has a default value: ``` hello(@Inject('user', {optional: true}) user: string = 'Mary') ``` Before this fix, the injected value for `user` is not used. The PR uses decorations to help decide the real number of params.
1 parent a82babf commit 801a82d

File tree

3 files changed

+27
-5
lines changed

3 files changed

+27
-5
lines changed

packages/context/src/resolver.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,15 @@ export function resolveInjectedArguments(
184184
const injectedArgs = describeInjectedArguments(target, method);
185185
const extraArgs = nonInjectedArgs || [];
186186

187-
const argLength = DecoratorFactory.getNumberOfParameters(target, method);
187+
let argLength = DecoratorFactory.getNumberOfParameters(target, method);
188+
if (argLength < injectedArgs.length) {
189+
/**
190+
* `Function.prototype.length` excludes the rest parameter and only includes
191+
* parameters before the first one with a default value. For example,
192+
* `hello(@inject('name') name: string)` gives 0 for argLength
193+
*/
194+
argLength = injectedArgs.length;
195+
}
188196

189197
let nonInjectedIndex = 0;
190198
return resolveList(new Array(argLength), (val, ix) => {

packages/context/test/acceptance/method-level-bindings.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ class InfoController {
1515
return msg;
1616
}
1717

18-
hello(@inject('user') user: string): string {
18+
hello(
19+
@inject('user', {optional: true})
20+
user: string = 'Mary',
21+
): string {
1922
const msg = `Hello ${user}`;
2023
debug(msg);
2124
return msg;
@@ -41,6 +44,15 @@ describe('Context bindings - Injecting dependencies of method', () => {
4144
expect(msg).to.eql('Hello John');
4245
});
4346

47+
it('injects optional prototype method args', async () => {
48+
ctx = new Context();
49+
ctx.bind(INFO_CONTROLLER).toClass(InfoController);
50+
const instance = await ctx.get(INFO_CONTROLLER);
51+
// Invoke the `hello` method => Hello Mary
52+
const msg = await invokeMethod(instance, 'hello', ctx);
53+
expect(msg).to.eql('Hello Mary');
54+
});
55+
4456
it('injects prototype method args with non-injected ones', async () => {
4557
const instance = await ctx.get(INFO_CONTROLLER);
4658
// Invoke the `hello` method => Hello John

packages/rest/test/acceptance/bootstrapping/rest.acceptance.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ describe('Bootstrapping with RestComponent', () => {
2020
let server: RestServer;
2121
before(givenAppWithUserDefinedSequence);
2222

23-
it('binds the `sequence` key to the user-defined sequence', async () => {
24-
const binding = await server.get(RestBindings.SEQUENCE);
25-
expect(binding.constructor.name).to.equal('UserDefinedSequence');
23+
it('binds the `sequence` key to the user-defined sequence', () => {
24+
// At this moment, the Sequence is not ready to be resolved
25+
// as RestBindings.Http.CONTEXT is not bound
26+
const binding = server.getBinding(RestBindings.SEQUENCE);
27+
expect(binding.valueConstructor.name).to.equal('UserDefinedSequence');
2628
});
2729

2830
async function givenAppWithUserDefinedSequence() {

0 commit comments

Comments
 (0)