Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

combineLatest throws TS2349 with 6 or more streams

This is my first foray into RxJS, so if there is a better solution I am all for it. My goal is to collect related objects and join them (essentially a sql left join) in code. This stems from me using an Angular In Memory API object to serve as the DB until such time it is established.

When I combine Plans, Specieses, Codebooks, UpstreamModalities, and TissueProcessions this code works as expected. When I include the Probes, Imaging, and Analysises streams I get a headache of a error blob to read. Essentially every line in the array.map function throws the TS2349 error, even the ones that worked before adding the remaining three sources.

import { Component, OnInit } from '@angular/core';
import { Plan } from '../plan';
import { PlanService } from '../plan.service';
import { DatabaseService } from 'src/app/serivces/database.service';
import { combineLatest } from 'rxjs';
import { map, tap } from 'rxjs/operators';

@Component({
  selector: 'app-plans-list',
  templateUrl: './plans-list.component.html',
  styleUrls: ['./plans-list.component.scss']
})
export class PlansListComponent implements OnInit {
  plans$ = combineLatest([
    this.planService.getPlans(),
    this.databaseService.getSpecieses(),
    this.databaseService.getCodebooks(),
    this.databaseService.getUpstreamModalities(),
    this.databaseService.getTissueProcessions(),
    this.databaseService.getProbes(),
    this.databaseService.getImagings(),
    this.databaseService.getAnalysises(),
  ])
    .pipe(
      tap(x => console.log(`Plans.join lookup tables: `, x)),
      map(
        // destructuring
        ([plans, 
          specieses, 
          codebooks, 
          modalities, 
          tissues,
          probes,
          imagings, 
          analysises
        ]) =>
          plans.map(plan =>
            ({
              ...plan,
              species: specieses.find(x => x.id === plan.speciesId).shortName,
              codebook: codebooks.find(x => x.id === plan.codebookId).codebookName,
              upstreamModality: modalities.find(x => x.id === plan.upstreamModalityId).SOPName,
              tissueProcession: tissues.find(x => x.id === plan.tissueProcessionId).SOPName,
              probe: probes.find(x => x.id === plan.probeId).SOPName,
              imaging: imagings.find(x => x.id === plan.imagingId).SOPName,
              analysis: analysises.find(x => x.id === plan.analysisId).SOPName,
            }) as Plan
          )
      )
    )


  constructor(
    private planService: PlanService,
    private databaseService: DatabaseService
  ) { }

  ngOnInit() {  }
}

Is this TS validation just choking up on the volume of streams? Since the code still compiles and executes as expected.

ERROR in src/app/plans/plans-list/plans-list.component.ts(40,11): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '(<U>(callbackfn: (value: Plan, index: number, array: Plan[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: StandardOperatingProcess, index: number, array: StandardOperatingProcess[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: Species, index: number, array: Species[]) => U, thisArg?: any) => U[...' has no compatible call signatures.
    src/app/plans/plans-list/plans-list.component.ts(43,24): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '{ <S extends Plan>(predicate: (this: void, value: Plan, index: number, obj: Plan[]) => value is S, thisArg?: any): S; (predicate: (value: Plan, index: number, obj: Plan[]) => boolean, thisArg?: any): Plan; } | { ...; } | { ...; } | { ...; }' has no compatible call signatures.
    src/app/plans/plans-list/plans-list.component.ts(44,25): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '{ <S extends Plan>(predicate: (this: void, value: Plan, index: number, obj: Plan[]) => value is S, thisArg?: any): S; (predicate: (value: Plan, index: number, obj: Plan[]) => boolean, thisArg?: any): Plan; } | { ...; } | { ...; } | { ...; }' has no compatible call signatures.
    src/app/plans/plans-list/plans-list.component.ts(45,33): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '{ <S extends Plan>(predicate: (this: void, value: Plan, index: number, obj: Plan[]) => value is S, thisArg?: any): S; (predicate: (value: Plan, index: number, obj: Plan[]) => boolean, thisArg?: any): Plan; } | { ...; } | { ...; } | { ...; }' has no compatible call signatures.
    src/app/plans/plans-list/plans-list.component.ts(46,33): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '{ <S extends Plan>(predicate: (this: void, value: Plan, index: number, obj: Plan[]) => value is S, thisArg?: any): S; (predicate: (value: Plan, index: number, obj: Plan[]) => boolean, thisArg?: any): Plan; } | { ...; } | { ...; } | { ...; }' has no compatible call signatures.
    src/app/plans/plans-list/plans-list.component.ts(47,22): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '{ <S extends Plan>(predicate: (this: void, value: Plan, index: number, obj: Plan[]) => value is S, thisArg?: any): S; (predicate: (value: Plan, index: number, obj: Plan[]) => boolean, thisArg?: any): Plan; } | { ...; } | { ...; } | { ...; }' has no compatible call signatures.
    src/app/plans/plans-list/plans-list.component.ts(48,24): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '{ <S extends Plan>(predicate: (this: void, value: Plan, index: number, obj: Plan[]) => value is S, thisArg?: any): S; (predicate: (value: Plan, index: number, obj: Plan[]) => boolean, thisArg?: any): Plan; } | { ...; } | { ...; } | { ...; }' has no compatible call signatures.
    src/app/plans/plans-list/plans-list.component.ts(49,25): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '{ <S extends Plan>(predicate: (this: void, value: Plan, index: number, obj: Plan[]) => value is S, thisArg?: any): S; (predicate: (value: Plan, index: number, obj: Plan[]) => boolean, thisArg?: any): Plan; } | { ...; } | { ...; } | { ...; }' has no compatible call signatures.
like image 812
Itanex Avatar asked Jul 24 '19 17:07

Itanex


2 Answers

This is true, maximum number of source Observables is 6, see https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/combineLatest.ts#L68.

However, The only reason is that it's unlikely to have so many inputs so there are no function overloads defined for that. This still doesn't restrict you from combining multiple combineLatests:

combineLatest([
  combineLatest([s1, s2, ...]),
  combineLatest([s7, s8, ...]),
]).pipe(
  map(([results1, results2]) => [...results1, ...results2]),
);

Btw, this is a TypeScript error that you should be able to suppress with @ts-ignore (even though you might miss other TypeScript errors later):

// @ts-ignore
plans$ = combineLatest([
  this.planService.getPlans(),
  this.databaseService.getSpecieses(),
  ...
  this.databaseService.getAnalysises(),
])
like image 121
martin Avatar answered Oct 13 '22 00:10

martin


Just for the sake of completeness (and having all the answer available on stackoverflow, and not in some link), the solution from the issue Balu linked is simply declaring the types each observable will emit:

combineLatest<any>(a$, b$, c$, d$, e$, f$).pipe(
  // Manually typed arguments below :(
  map(([a, b, c, d, e, f]: [string, number, string, Foo, Bar, Baz]) => doStuff(a, b, c, d, e, f))
)
like image 29
Moshezauros Avatar answered Oct 13 '22 00:10

Moshezauros