Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 423
Krzysztof Przybylski Avatar asked Sep 02 '25 04:09

Krzysztof Przybylski


1 Answers

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();
like image 69
AT82 Avatar answered Sep 05 '25 02:09

AT82