-
Notifications
You must be signed in to change notification settings - Fork 27k
Description
In strongly typed languages such as Java or C# we are used to program to an interface.
It allows loosely coupled components and also it allows easier unit testing with mock implementations of the interface (contract).
In TypeScript the interfaces have some interesting features.
- structural typing which is "duck typing" inference of interface, even if the class of the object it not marked with it.
- ambient declarations which allow you to describe 3rd party API with an interface, after the fact of the creation.
- therefore the TS class could explicitly or implicitly implement multiple interfaces. Therefore interface could not be substituted by abstract class. Specially with prototype inheritance of underlying JavaScript.
- interfaces in typescript are compile time only construct, useful for compiler to warn you that the (argument) doesn't meet the (API) expectation.
The fact that interfaces do not exist in form of IIFE/class as product of TypeScript compilation is the root of problem, how to use it as the DI marker/handle.
Possible solutions:
- generate dummy class for each interface, but do not try to cast to it (whatever it means in JavaScript). Benefit is that IIFE could contain RTTI, downside TypeScript compiler would have to implement this. Would the RTTI of classes which explicitly implemented the interface know about it ? Would there be class.GetInterfaces():type[] ?
- capture just the interface name as string and use it as DI handle.
- allow for manual annotation of dependencies
More about DI + interfaces vs autowiring with dependencies by class
- DI is implementation of IoC pattern. The goal is to externalize the knowledge about composition and implementation of the contract.
- Autowiring of injection of specific class is just special case of IoC.
- The general case of DI with interfaces needs to be configured explicitly in order to choose the correct implementation of the interface.
Therefore I think we need a way how to register which implementation of interface would be injected.
In many DI frameworks people use fluent API of the DI container to configure it. Sample below is Microsoft Unity which uses RTTI of C# generics for the capture.
- just class registration
container.RegisterType<InvoicesService>(); - class as implementation of interface
container.RegisterType<IInvoicesService, InvoicesService>(); - sometimes the interface type is not granular enough and we need name as well
container.RegisterType<IInvoicesService, InvoicesService>("Czech"); - sometimes the implementation is statefull and we need lifetime
container.RegisterType<IInvoicesService, InvoicesService>(new ContainerControlledLifetimeManager()); - sometimes the singletons need scope, for example per request. There are 2 approaches child container or scoped resolve operation.
More about intended usage:
In strongly typed languages we impose type validating compiler on our-self because the codebase is too big to remember it all.
In this case we are interested to continue usage of TypeScript, which bring this quality to JavaScript.
So if this feature is not easily doable as extension of TS compiler, we would need to find easy way how to achieve same result with manual annotation.
Hope this helps, sorry if I'm not up-to-speed with all details or visions of Angular 2.0 or di component.
-- Pavel