I am looking for a better way to satisfy Typescript's typesystem when using `merge' and 'scan' together.
The usecase is updating a list, initially retrieved from the server, with items created on the client.
I've something like
class ProcessService {
private processes$: Observable<Process[]>;
private newProcess$: Subject<Process> = new Subject<Process>();
private routes$: Observable<any>;
ngOnInit(): void {
this.processes$ = merge(this.routes$.pipe(
switchMap(route => this.getProcesses(route))),
this.newProcess$).pipe(
scan(this.combineProcesses));
}
// Problem: both params could be array or item
private combineProcesses(accProcesses, additionalProcess) {
const prevAcc = Array.isArray(accProcesses) ? [...accProcesses] : [accProcesses];
const newProcess = Array.isArray(additionalProcess) ? [...additionalProcess] : [additionalProcess];
return prevAcc.concat(newProcess);
}
private getProcesses(client: string) {
// actuall a http-call
return of([1, 2, 3]);
}
private addNewProcess(process: Process) {
this.newProcess$.next(process);
}
}
Merge
's type is Observable<Process | Process[]>
and I want scan
to return a Observable<Process[]>
.
I am sure the implementation of combineProcesses
can be improved. Or does something else need to change?
CodePudding user response:
You could store all new processes with a BehaviorSubject.
private newProcesses$: BehaviorSubject<Process[]> = new BehaviorSubject([]);
private addNewProcess(process: Process) {
this.newProcesses$.next([... this.newProcesses$.value, process]);
}
CodePudding user response:
What I would do is to set a starting value for the accumulator in the scan function like this
scan(this.combineProcesses, [])
If you do something like this you can change the assumption that both parameters of the combineProcesses
function can be of type Process | Process[]
.
In fact, adding an array as the starting value of the accumulator brings to the fact that the accumulator can be only of type Process[]
while the new value received by the function passed to scan
can actually be of type Process | Process[]
.
If you do this, this.processes$
can actually be an Observable<Process[]>
.
This is the whole code
type Process = { id: number };
class ProcessService {
private processes$: Observable<Process[]>;
private newProcess$: Subject<Process> = new Subject<Process>();
private routes$: Observable<any>;
ngOnInit(): void {
this.processes$ = merge(
this.routes$.pipe(switchMap((route) => this.getProcesses(route))),
this.newProcess$
).pipe(scan(this.combineProcesses, []));
}
// Problem: both params could be array or item
private combineProcesses(
accProcesses: Process[],
additionalProcess: Process | Process[]
) {
const prevAcc = Array.isArray(accProcesses)
? [...accProcesses]
: [accProcesses];
const newProcess = Array.isArray(additionalProcess)
? [...additionalProcess]
: [additionalProcess];
return prevAcc.concat(newProcess);
}
private getProcesses(client: string) {
// actuall a http-call
return of([{ id: 1 }, { id: 2 }, { id: 3 }] as Process[]);
}
private addNewProcess(process: Process) {
this.newProcess$.next(process);
}
}
CodePudding user response:
This should get the types to line up:
...
scan(this.combineProcesses, [] as Process[])
...