-
Notifications
You must be signed in to change notification settings - Fork 27.4k
fix($inject): enhance annotate function to work with inheritance #16344
Conversation
The annotate function now respects prototypical (ES5) or class (ES6) inheritance so that you can use these concepts for controllers without issues. Test cases to ensure proper functionality included.
Hi @rose-m. Thanks for submitting this PR. This is an area we have had lots of discussion about in the past. |
Thanks @petebacondarwin for your comment - I hope I understood the discussion and will outline my approach. Let's take a look at the following scenarios: Inheritance with overridden constructor with own parameters:
My changes to the annotate function will result in the following behavior:
Inheritance with no overridden constructor/overridden constructor without arguments:
Now the annotate function will do the following:
I hope this outlined my approaches to the problems mentioned in the other comments. I agree that it is not possible to find out whether the constructor is overridden or not - but that is where my assumption from point 4. above comes in: I just use the super-classes annotation since I don't see it possible to forward anything (which is required) to the super class. |
The approach looks reasonable, but unfortunately (as mentioned in the linked discussions) there are many different usecases and there is no way to support all of them. The good news is thaat in all cases, explicitly annotating all functions works correctly (so there is always a way to handle a particular usecase). So, at this point, I think it makes sense to strive for (a) avoiding breaking changes and (b) aiming to make the most common usecases simple (which I think it already were we're at - could be wrong though). Note, that we always need to take the following implementations into account:
Off the top of my head, the proposed solution will fail in the case where an injectable class is extending a class which is not supposed to be injectable. E.g.: class ApiClient {
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
...
}
myModule.service('FooApiClient', class FooApiClient extends ApiClient {
constructor() { super('/foo/api'); }
});
myModule.service('BarApiClient', class BarApiClient extends ApiClient {
constructor() { super('/bar/api'); }
}); In this case, the injector will look up the prototype chain and try to inject |
Hey @gkalpak - thank you for pointing this out, definitely something I didn't recognize properly in the other discussion, sorry :( So once more my PR was just a deliberate last battle cry for help to an unsolvable problem :D I much appreciate your responses! |
@rose-m - I'm sorry that this appears intractable. The good news is that manual annotation always works! |
The annotate function now respects prototypical (ES5) or class (ES6)
inheritance so that you can use these concepts for controllers
without issues. Test cases to ensure proper functionality included.
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
It changes how the
$inject
information is retrieved from a function/class in order to work with (prototypical) inheritance.What is the current behavior? (You can also link to an open issue here)
When using ES6 classes and the super-class is annotated first, annotating the sub-class will always return the information of the super-class. If the sub-class requires different injections this will not work.
What is the new behavior (if this is a feature change)?
Now the annotate function respects an existing inheritance chain and computes the
$inject
array correctly.Does this PR introduce a breaking change?
No
Please check if the PR fulfills these requirements
Other information:
No