Understanding The Role Of Static Methods In An Angular 2 Dependency-Injection Context
In the Angular 2 dependency-injection (DI) framework, Angular maps tokens onto singleton instances of JavaScript "classes." This allows us to swap in and out various implementations of a class so long as all of the other classes agree on which DI token is needed to reference the abstract implementation. Each concrete implementation of a the given class token then has to adhere to the interface inherent in the DI token contract. But, classes can have both static and instance methods; so, which of these need to be implemented by a concrete class in a dependency-injection context?
At first, you might think that both static and instance methods are part of the class "contract" in a dependency-injection context. After all, if you're trying to swap in and out various implementations, it's natural to think that the entire topology of the swappable classes needs to be interchangeable.
And, you'd be right - if we were actually dealing with "classes." But, in a dependency-injection framework, like Angular 2, we're not dealing with classes, we're dealing with instances of classes. This is a very important difference. In JavaScript (and therefore in Angular 2), class instances don't include static methods in their public API. As such, the set of static methods exposed on a constructor is not part of the "API contract" bound to each instance created by that constructor.
It might be easier to think about this divergence outside of a dependency-injection framework. So, let's look at the native Array class in JavaScript. While the Array class exposes static methods, like isArray(), those static methods cannot be accessed on each array instance:
|
|
|
||
|
|
|||
|
|
|
What this means for consumers of the Angular 2 dependency-injection framework is that when you need to implement a given class interface, you only need to implement the instance methods of that class. Since the dependency-injection framework only deals in instances, no injected dependency will ever expose a static method (see epilogue). Therefore, static methods are not part of the contract inherent in a dependency-injection token.
Epilogue: The Angular 2 dependency-injection framework deals primarily with class instances, but not exclusively. As such, it is most certainly possible to make the DI framework provide a class constructor as opposed to a class instance. In that case, the injected value would expose static methods since the injected value is a class and not an instance of a class. But, that's not really relevant to this discussion.
Reader Comments
Hi Ben,
again nice deep overview about more, kind of complex, ng2 behind the scenes concepts.
Providing a class constructor for ng2 DI is a no brainer, because ng2 is so awesome :)
I've created a plunk to demonstrate
http://plnkr.co/edit/z0uYfrJw5yfuFMW7kgHY?p=preview
@All,
On Twitter, Darien (@bhathos) pointed out the fact that you can technically gain access to the Constructor "static" methods using the the .constructor property on the class instance. Using the Array example, you could technically do something like:
var a = [];
a.constructor.isArray( "blam" );
.... but, in my opinion, the .constructor property should be treated more like meta-data and less like a property of the instance itself. For example, I think it's fine to assume that the .constructor property can power the "instanceof" operator (and to define the constructor property when outlining your prototype). But, beyond that, I wouldn't really depend on it too heavily.
But, that's just my opinion.
@Martin,
Excellent example. For anyone who is not exactly sure what Martin is demonstrating, in his provider collection, he's using:
provide( "FooClass", { useValue: Foo } )
This is telling Angular 2 to use the *CLASS* Foo as the injectable, as opposed to *instantiating* the class Foo and using the *instance* as the injectable. This way, you could use either the static or the instance methods; but, they would be attached, so to speak, to two different injectables.
God bless you for such a clear explanation.
Any thought on how to disable webstorm "method can be static" message?