8

I had a discussion today where some of my colleagues said that they inject their Angular services like that:

constructor(readonly language: I18nService)

They said they do this because it prevents consumers of my component to change the injected service, kinda like that:

@Component({ ... })
class ComponentA {
    constructor(public language: I18nService) {}
}

@Component({ ... })
class ComponentB {
    @ViewChild(ComponentA) compA: ComponentA;
    constructor() {
        this.compA.language = new I18nService();
    }
}

So, while technically they are right I'm still not convinced that I should do it that way. I ask myself the following question:

  1. DI is a fundamental part of Angular. If someone really does this, should this person better run into this pit and fail or should he/she not be able to do this at all

  2. readonly in this situation might be pretty complex to understand for someone who starts learning Angular and TypeScript for a couple of reasons

    • Angular doesn't use this approach in any of their official DI docs
    • You need to know how readonly works and that it just protects the reference of my injected service but none of the properties
  3. In my opinion, it is a corner case problem, even though there's a simple solution to it

What do you think? Are there any official references I might not have seen? I haven't found anything when I tried to google for the usage of readonly in Angular concepts

One last word: While it is 100% true - It is possible to manipulate a reference of public service: Service - I'm still not sure if this should be solved at all and struggle whether to do it or not.

Sasuke Uchiha
  • 421
  • 6
  • 17
kair
  • 946
  • 1
  • 10
  • 16
  • 2
    At risk of being closed for being opinion based, I'd suggest adding that you are looking for official references. I typically use `private: myService: MyService` to solve this problem. – Wilhelmina Lohan Feb 25 '19 at 17:57
  • Thanks, you are right. I added this at the second-last paragraph! There really might be official references I haven't found yet. – kair Feb 25 '19 at 18:11
  • 1
    I second @WilliamLohan. I always define services as `private` in the constructor. – Brandon Taylor Feb 25 '19 at 18:14
  • 2
    I can't find any documentation that explicitly states it but I assume all the angular docs inject private bc DI gives that component the service for it alone and anything else should injects its own service. Also semi related https://stackoverflow.com/questions/46596399/typescript-dependency-injection-public-vs-private – Wilhelmina Lohan Feb 25 '19 at 18:17
  • 1
    I usually make the service, and other injected items, private for encapsulation and high cohesion. The component should expose what is necessary for it to work. See also https://stackoverflow.com/q/3085285/1260204, this is not really specific to angular IMO. – Igor Feb 25 '19 at 18:17
  • 1
    I guess there is nothing wrong with readonly, just that only making it readlony doesn't solve the issue with the code above. If `ComponentB` is getting `I18nService` from `ComponentA` you are undermining DI and `ComponentB` becomes tightly coupled and could never be unit tested w/o `ComponentA` – Wilhelmina Lohan Feb 25 '19 at 18:28
  • Angular has `public` services in their docs https://angular.io/guide/dependency-injection-in-action but I'm unsure if this is on purpose or just a documentation issue – kair Feb 25 '19 at 18:36
  • 1
    The `readonly` doesn't actually make it readonly. It just means that Typescript will not let anyone external change it. `private` should accomplish the same thing. I think that the `private service: Service` is the way that we should stick here. I will add, if someone could show me an advantage of using the readonly, I would consider adopting it. But the reasons provided thus far is not a real reason. – frosty Feb 25 '19 at 19:13
  • My thoughts on it are that sometimes you want to use a DI service in the component template (to avoid writing a wrapper function for each piece you need). You have to mark it public to use it in a template. If you are making it public, someone could accidentally replace it (shame on them). But if you mark it readonly, this will remind future developers not to. However, I am also looking for evidence that this is a good practice or not. – Troy Weber Mar 19 '20 at 16:16

2 Answers2

7

It is not something that you have to do, but it is good practice to make them readonly since you probably don't want to reassign the instance again.

Read-only properties may have initializers and may be assigned to in constructors within the same class declaration, but otherwise, assignments to read-only properties are disallowed.

Sasuke Uchiha
  • 421
  • 6
  • 17
4

you can add private readonly service : ServiceClass

elcordova
  • 106
  • 6
  • 1
    What is the reasoning behind this? – sin2akshay Mar 24 '22 at 11:19
  • Whats the reason – tushah Jun 16 '22 at 17:08
  • 1
    The reason for this is to limit anyone from reassigning the same object reference within the class where it is injected. For example you have private someService: SomeService. Inside any other function than your constructor you can set someService to a new instance (someService = new SomeService()). The readonly will prevent this. – Hobbes Jul 19 '22 at 13:21