angulartypescriptrxjs

Encapsulate behaviorSubject in a service


I would like to hide the "next" interface of a BehaviorSubject while retaining its other properties, especially the possibility to get last emitted value imperatively. The idea is all state manipulation can be done on a private BehaviorSubject within a service and all I expose is what I would call a "ReadonlySubject" that does not have "next" method. The only idea I have so far is something like this:

export class ReadonlyBehaviorSubject<T> extends BehaviorSubject<T> {
  override next(_value: T) {
    console.error('cannot set value on a readonly BehaviorSubject');
  }
}

export function toReadonlyBehaviorSubject<T>(bs: BehaviorSubject<T>): ReadonlyBehaviorSubject<T> {
  return new ReadonlyBehaviorSubject(bs.value);
}

As you can see we still have "next" method - it just does nothing, but informs the user that they cannot set a value on ReadonlyBehaviorSubject. Is there something neater? Preferably not exposing "next" method at all?


Solution

  • You are just overriding the next method, so it is still available. The correct way would to omit "next". Create a type instead:

    export type ReadonlyBehaviorSubject<T> = Omit<BehaviorSubject<T>, 'next'>;
    

    Then use it like:

    private data = new BehaviorSubject<string>('some value');
    public readonly data$: ReadonlyBehaviorSubject<string> = this.dataSubject;
    

    But as mentioned in comments, what I usually do is to have the subject as private and expose an observable to subscribe to:

    private data = new BehaviorSubject<string>('some value');
    public data$ = this.data.asObservable();