Home > Software design >  RxJS Typescript: better way to handle type when using merge scan
RxJS Typescript: better way to handle type when using merge scan

Time:07-08

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[])
...
  • Related