iCV
iCV

Reputation: 607

Forkjoin followed by foreach causes 'Cannot invoke an expression whose type lacks a call signature.'

When trying to subscribe to the result of a forkJoin inside the onInit function, I get the following error:

error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((next: (value: Result[]) => void, promiseCtor?: PromiseConstructorLike) => Promise) | ((next: (value: Account[]) => void, promiseCtor?: PromiseConstructorLike) => Promise)' has no compatible call signatures.

getAccounts returns an observable of Account[], getResults returns an observable of Result[]. When both are finished loading, I'm trying to get the detailed account results from another database, using the accessToken of the accounts retrieved from the getAccounts function, so that I can add the results to the detailed accounts.

In short I'm trying to achieve this:

let var = getAccountsSimplified();
let var2 = getAllResults();

if both are finished{
    foreach(acc in var) {
        account = getDetailedAccount(acc.id)
        var ownedResults = var2.where(userid === acc.id)
        account.results = ownedResults
    }
}

This is what I tried so far, but for some reason it is causing the error mentioned above. For some reason the console.log(user) is working fine, which is why I'm completely lost at the error.


const tasks$ = [
      this.aus.getAccounts(this.auth.token),
      this.rs.getResults(this.auth.token)
    ];

    forkJoin(tasks$).subscribe(
      value => {
        const userList = value[0];
        const resultList = value[1];
        userList.forEach((acc: Account) => {
          this.aus.getUserDetails(acc.access_token).subscribe((user: User) => {
            if (user != null) {
              console.log(user);
            }
          });
        });
      }
    );

Signatures of called functions:

  getAccounts(auth: string): Observable<Array<Account>> {
  }

  getResults(auth: string): Observable<Array<Result>> {
  }

  getUserDetails(token: string): Observable<User> {
  }

Update
After adding Kurt's suggestion, I'm now getting

Argument of type 'MonoTypeOperatorFunction<[Account[], Result[]]>' is not assignable to parameter of type 'OperatorFunction<(Observable | Observable)[], [Account[], Result[]]>'. Types of parameters 'source' and 'source' are incompatible. Type 'Observable<(Observable | Observable)[]>' is not assignable to type 'Observable<[Account[], Result[]]>'. Type '(Observable | Observable)[]' is missing the following properties from type '[Account[], Result[]]': 0, 1ts(2345)

It seems as if there is a problem with the returned type of the forkjoin.

Update 2
Turns out removing the tasks$ array and adding the functions directly to the forkjoin solved the problem.

Upvotes: 0

Views: 230

Answers (1)

Kurt Hamilton
Kurt Hamilton

Reputation: 13515

Try converting your nested subscription into a switchMap.

I have refactored your example to:

  1. Save the resultList out to some variable or property - that can be used later
  2. Pass the userList onto a switchMap
  3. Use switchMap to forkJoin all of the calls to getUserDetails
  4. The array of user details now arrives in your subscribe, and you have access to the result list
const tasks$ = [
  this.getAccounts(),
  this.getResults()
];

forkJoin(tasks$).pipe(
  tap((value: [ Account[], Result[] ]) => this.resultList = value[1]),
  map((value: [ Account[], Result[] ]) => value[0]),
  switchMap(accounts => forkJoin(
    accounts.map(x => this.getUserDetails(x.access_token))
  ))
).subscribe(details => {
  this.userList = details.filter(x => !!x);
  console.log(this.resultList);
});

This example is from my demo below, which demonstrates the concept rather than using your exact calls and setup.

DEMO: https://stackblitz.com/edit/angular-viewbm

Upvotes: 1

Related Questions