22

I am learning RxJs, I am seeking confirmation or correction on my assumption.

I am trying to make a public read only observable in a service that I can use .next() on in various places in my service class. I am wondering if this is the proper way to do it:

private myObservable = new Subject<T>();
public myObservable$: Observable<T> = this.myObservable.asObservable();
  • The user can subscribe to myObservable$
  • I can usemyObservable.next(...);

It works perfectly but I am experience enough to know that I may just be being an unwitting idiot (RxJS is huge). Is this correct pattern and correct object for said use case?

martin
  • 93,354
  • 25
  • 191
  • 226
  • 2
    Yes, this is a common and correct pattern. But in many cases, it is not needed. You only really need a Subject if you need to send notifications to components that are *not* binding. If you are binding, Angular's change detection can take care of handling notifications. I have an example with Subject/BehaviorSubject AND matching code with the same functionality without it here: https://github.com/DeborahK/MovieHunter-communication (MH-Take4 and MH-Take5) – DeborahK Feb 22 '18 at 22:35
  • 1
    @DeborahK Based on some of the comments on some of the replies in this thread I am very curious what your thoughts are on the use of private in TypeScript. Should one not write code using private because it is in fact not private, should one write code in typescript as if they are writing C# and simple ignore the fact that the transpile treats both private and public the same? –  Feb 23 '18 at 00:44
  • 2
    The entire point of TypeScript is to give us coding assistance/productivity by giving us types, interfaces, and accessibility key words (such as `private`). If we weren't going to use these things ... there would be no point in using TypeScript. The fact that all of this is transpiled out doesn't matter. It all becomes 1's and 0's at some point. :-) – DeborahK Feb 23 '18 at 01:42
  • @DeborahK however, as TypeScript compiles to JavaScript, and the latter will shortly include private properties that are completely, both syntactically and semantically incompatible, yet provides the much stronger guarantees that we would wish for, I think it's a poor practice to use `private` properties _now_ that this is known. Also I don't think the 1s and 0s points is relevant like it is, for example, in the case of C vs C++. On the contrary, I find rather often, a desire to treat typescript as a language that offers higher runtime abstractions, when it emphatically does not by design. – Aluan Haddad Feb 23 '18 at 19:11
  • 1
    You are correct. TypeScript provides higher *development time* abstractions ... it does nothing really for runtime. – DeborahK Feb 23 '18 at 19:15
  • 1
    @DeborahK the purpose of typescript, as stated by its designers, is to provide a statically verifiable formalization of JavaScript's implicit type system. Tangentially, while programmers using a variety of frameworks, wish to pretend that it describes a Java like type system, this mistake is by far most commonly observable among the Angular community. There's long been unwillingness to learn JavaScript among many developers, and thinking that TypeScript offers an _alternative_ to JavaScript is serious wrong. With your background in JS, and role as an educator, you're no doubt aware of this. – Aluan Haddad Feb 23 '18 at 19:36

2 Answers2

19

What you're doing is correct. There's however still a little shorter notation. Since Subject is already an Observable (it inherits the Observable class) you can leave the type checking to TypeScript:

private myObservable = new Subject<T>();
public myObservable$: Observable<T> = this.myObservable;

Any consumer of your service can subscribe to myObservable$ but won't be able to call myObservable$.next() because TypeScript won't let you do that (Observable class doesn't have any next() method).

This is actually the recommended way of doing it and RxJS internally never uses asObservable anyway. For more detailed discussion see:

See a very similar question: Should rxjs subjects be public in the class?

martin
  • 93,354
  • 25
  • 191
  • 226
  • 3
    Correct second line is `myObservable$ = myObservable.asObservable();` which doesn't have a `next` method at all and is also of type of `Observable` – Aluan Haddad Feb 22 '18 at 21:08
  • 1
    The `Observable` type is enforced by TypeScript. The only way you could avoid it is using `(myObservable$ as any).next()` which you typically never do. Read the two GitHub links why RxJS internally always uses this way. – martin Feb 22 '18 at 21:10
  • then why do they provide `asObservable`. This is a weak approach. – Aluan Haddad Feb 22 '18 at 21:12
  • Read the two GitHub links and you'll find the answer. – martin Feb 22 '18 at 21:12
  • I did read them but it doesn't I mean I agree. First of all it's debated in the issue itself. I believe they made the wrong choice. Subjects are a massive hammer. Ben Lesh himself has written about how using them is an auntie pattern and he's exposing subjects for performance reasons. Also note code within the library has different performance it concerns. – Aluan Haddad Feb 22 '18 at 21:14
  • @AluanHaddad "misuse of a type system" and "very misleading" is a hell of a thing to say, it is built into typescript and I agree it is a bit misleading of them. Think about that for a second. I think we TypeScript devs all know what private does in typescript by now. I am using it as intended, to imply intent to other developers on the team. TypeScript will inform and complain and make my intent known. I think, maybe, I am dealing with someone who recently learned that private is not private in the output and felt misled has a need to freak out about it. Or maybe I am abusing typescript. –  Feb 22 '18 at 22:51
  • 1
    All right it's a bit harsh but it's a false sense of security. The thing is that people tend to forget that type assertions are not casts because they look like casts. Anyway you've got the RX Js people backing you up although one of them disagreed pretty strongly. – Aluan Haddad Feb 22 '18 at 23:09
  • But it's not an abuse I take it back. It's just an inelegant and unnecessary optimization at the app level. Realistically you're going to have a couple of these observables in singleton services and the overhead of using the as observable is not something that would be noticed. Personally, if the service is a singleton, I declare the subject at module scope. – Aluan Haddad Feb 22 '18 at 23:12
  • 1
    If you are really worried about safety, I would also recommend that you use a getter to receive the `Observable`. Otherwise someone could overwrite it with his own `Subject` and start pushing unwanted data. – Markai Feb 23 '18 at 13:20
  • 1
    The standard approach is to use `asObservable`, which doesn't provide `next`. Those `github` links aren't relevant for consumers of the library, that's an internal micro-optimization discussion. Third party libraries used in functional reactive programming (e.g ramda) often lose the type information during composition, so `next` could still be called if it's not `asOvservable` – Drenai Aug 16 '19 at 06:57
  • 1
    I think what may be somewhat underappreciated here is how "type widening" methods like `asObservable` can help reduce syntactic noise: `public myObservable$ = this.myObservable.asObservable();` Here, the type annotation for myObservable$ is omitted because its intended type is statically deduced, thus reducing syntactic noise. At the same time, `asObservable()` expresses intent much more explicitly than (somewhat ironically) implicit type widening by an explicit type annotation. Possible performance impact of `asObservable` is a pretty moot point unless you are building framework. – chris Feb 22 '20 at 11:00
3

In project we are using this kind of Observables, this is giving you proper encapsulation to your private observable, but you still can call next() using some public method.

      private sourceName = new Subject<T>();
      name = this.sourceProductName.asObservable();

      sendName(item: T) {
        this.sourceName.next(item);
      }
Kraken
  • 1,905
  • 12
  • 22
  • 1
    You used BehaviorSubject, which I do not need in my use case. and added a public method thereby negating the encapsulation of broadcasting. –  Feb 22 '18 at 20:03
  • 1
    I doesn't matter you can replace it with just `Subject`, i am using this to sometimes retrieve value of this Observable in some period of time with `getValue()` It will fit your needs. – Kraken Feb 22 '18 at 20:08
  • Sorry i edited and left initial value there, `Subject` don't need that. – Kraken Feb 22 '18 at 20:12
  • You literally copied my code and then broke it by adding a public method accessor to the private variable to remove the encapsulation. I think you may have misunderstood the question. If you read your code it can be replaced with `name = new Subject();` well, you did protect everything except `next()`, based on that I think you just misread my post. I think what this is coming down to is: my assumption in my OP is was correct. But thank you for taking a poke at it for me. –  Feb 22 '18 at 20:26
  • @EddieMarinaro there is no such thing as privacy via `private` so this is hardly broken. I agree its kind of pointless to simply wrap that method. The point of `asObservable` is to hide the subject but it's a different sort of hiding – Aluan Haddad Feb 22 '18 at 21:03
  • I understand the generated code is not truly private, it is however private when working with typescript, the compiler will provide warnings. At the end of the day that is one of the purposes of TypeScript to infer the intent, I can think of plenty that can break in typescript. `myNumber: number` is not truly a number, but we know that. We use it for dev time and know that we must also provide other caution to validate said variable outside of typescript. thus, `private is private` in typescript as much as `number is number` I too would like to see some type of Revealing Module Pattern added. –  Feb 22 '18 at 22:30
  • @EddieMarinaro private will soon be a problem because ECMAScript is introducing actual private properties with a different syntax but true privacy. It's going to confuse a lot of people. I never use private anymore – Aluan Haddad Feb 22 '18 at 23:15
  • @AluanHaddad I am sure that will work out perfectly. –  Feb 22 '18 at 23:18
  • @EddieMarinaro I sincerely hope so but given that they have to be polyfilled with weak maps, have different lifetimes and initializations and access rules there is no way they can be unified – Aluan Haddad Feb 23 '18 at 00:44