fix(core): correct misunderstanding of optional metadata #15137
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Issue Number: resolves #2581
Currently, Nest does not throw an error even when a child class has incomplete dependencies due to an optional parameter declared in a parent class.
To demonstrate the reason for this behavior, here is a simplified reproduction:
In this example, I created a decorator that behaves the same way as in NestJS to test the behavior.
Looking at the current state, design:paramtypes reflects only the parameters of the respective class.
So for Parent, it contains class Foo, whereas for Child, it returns an empty array.
(This is because the constructor overrides the metadata. If Child did not define its own constructor, it would inherit class Foo just like Parent.)
However, in the case of the
@Optional()
decorator, the metadata is not overridden and is inherited by Child as is.This difference in how metadata is inherited leads to the core issue.
At first, I suspected that this might be caused by the mixin mechanism, but it turns out the actual issue was a misunderstanding of how Optional metadata behaves.
Let’s look into what actually happens, based on the reproduction:
nest/packages/core/injector/injector.ts
Lines 387 to 399 in eab3910
The
Injector
retrieves bothparamtypes
andoptional
metadata.In this case, the constructor parameters reflect the new
JwtAuthGuard
, soparamtypes
would contain[ [class JwtAuthGuard] ]
.However, due to the
@Optional
decorator declared in the parent class (e.g.,AuthGuard
), [0] ends up in the optional metadata.nest/packages/core/injector/injector.ts
Lines 325 to 331 in eab3910
As a result, the first parameter of
JwtAuthGuard
is mistakenly treated as optional.This causes Nest to silently ignore the missing dependency—even though it’s not imported by the module—because it assumes the parameter is optional.
Ultimately, this does not appear to be an issue exclusive to the use of mixins.
What is the new behavior?
Changed from
getMetadata
togetOwnMetadata
when retrieving optional parameters in a simpler way.Now, the optional status of a parameter is determined solely by its own
@Optional
, without mistakenly inheriting the@Optional
from its parent.Does this PR introduce a breaking change?
Other information