I am working on a class in Angular and want to share a bunch of states belong to that class. So I make a bunch of BehaviorSubject
private subjects = {
a : new BehaviorSubject<A>(this.a),
b : new BehaviorSubject<B>(this.b),
c : new BehaviorSubject<C>(this.c),
d : new BehaviorSubject<D>(this.d),
e : new BehaviorSubject<E>(this.e),
}
To prevent leaking the Observer
side of the subjects and only expose the Observable
side, I make the subjects private and expose with observables:
observables = {
a : this.subjects.a.pipe(share()),
b : this.subjects.b.pipe(share()),
c : this.subjects.c.pipe(share()),
d : this.subjects.d.pipe(share()),
e : this.subjects.e.pipe(share()),
}
I think the observables should be able to be generated from the subjects so that when I want to add more subjects, I don't need to modify the observables manually. Something like:
observables = (()=>{
let observables : {[Property in keyof typeof this.subjects]:Observable} = {}
for(let key in this.subjects)
{
observables[key] = this.subjects[key as keyof typeof this.subjects].pipe(share())
}
return observables;
})();
But here Observable
and share
do not know their generic type. How can I make this work or if there is better pattern?
As far as I am aware you don't need to share
the BehaviorSubject
observable as it is already hot via the BehaviorSubject
.
You can solve the generic typing issues by using Mapped types:
const subjects = {
a: new BehaviorSubject<number>(1),
b: new BehaviorSubject<string>('foo'),
c: new BehaviorSubject<boolean>(false),
};
// utility type to convert BehaviorSubject<T> to Observable<T>
// (can be inlined below instead)
type BehaviorSubjectToObservable<T> =
T extends BehaviorSubject<infer U> ? Observable<U> : never;
// create appropriate type for resulting object which has the same keys
// but maps to Observable<T>
// this ensures correct typing within the reduce
type Observables<T> = {
[P in keyof T]: BehaviorSubjectToObservable<T[P]>;
};
// transform the object
const observables = Object.entries(subjects).reduce(
(acc, [key, subject]) => ({ ...acc, ...{ [key]: subject.asObservable() } }),
{} as Observables<typeof subjects>,
);
// typeof observables.a = Observable<number>
// typeof observables.b = Observable<string>
// typeof observables.c = Observable<boolean>