Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeScript interfaces for Dependency Injection #135

Closed
pavelsavara opened this issue Oct 31, 2014 · 7 comments
Closed

TypeScript interfaces for Dependency Injection #135

pavelsavara opened this issue Oct 31, 2014 · 7 comments

Comments

@pavelsavara
Copy link

@pavelsavara pavelsavara commented Oct 31, 2014

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:

  1. 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[] ?
  2. capture just the interface name as string and use it as DI handle.
  3. 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

@mhevery mhevery added the #DI label Nov 4, 2014
@mhevery
Copy link
Member

@mhevery mhevery commented Nov 4, 2014

@vsavkin

Our current thinking is to go with

  1. 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[] ?

The syntax for this should already be supported with

var injector = new Injector([
        Car,
        bind(Engine).toClass(CyclicEngine)
      ]);

Is there an actual Action Item here? I think we already support this so I don't think there is anything to do.

@mhevery mhevery modified the milestone: RFC / Discussion Nov 4, 2014
@pavelsavara
Copy link
Author

@pavelsavara pavelsavara commented Nov 5, 2014

Is there unit test where I could have look at it ?

@vsavkin
Copy link
Contributor

@vsavkin vsavkin commented Nov 5, 2014

@pavelsavara

@mhevery
Copy link
Member

@mhevery mhevery commented Nov 6, 2014

Closing since I believe this is covered.

@pavelsavara
Copy link
Author

@pavelsavara pavelsavara commented May 6, 2015

This doesn't work very well in typescript 1.5beta, see microsoft/TypeScript#3060

@mhevery
Copy link
Member

@mhevery mhevery commented May 8, 2015

The issue is that Types don't have a reference at runtime. But I believe MS will solve it. Nothing for Angular to do here.

@angular-automatic-lock-bot
Copy link

@angular-automatic-lock-bot angular-automatic-lock-bot bot commented Sep 6, 2019

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 6, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants
You can’t perform that action at this time.