Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reduce operator typing gets lost #4086

Closed
bridzius opened this issue Sep 3, 2018 · 3 comments
Closed

reduce operator typing gets lost #4086

bridzius opened this issue Sep 3, 2018 · 3 comments
Labels
TS Issues and PRs related purely to TypeScript issues

Comments

@bridzius
Copy link

bridzius commented Sep 3, 2018

Bug Report

Current Behavior
When using reduce/scan, typings get lost on 6.3.1 version of rxjs

index.ts:5:27 - error TS2345: Argument of type 'MonoTypeOperatorFunction<any[]>' is not assignable to parameter of type 'OperatorFunction<string, any[]>'.
  Types of parameters 'source' and 'source' are incompatible.
    Type 'Observable<string>' is not assignable to type 'Observable<any[]>'.
      Type 'string' is not assignable to type 'any[]'.

5   return from(items).pipe(reduce((acc, item) => acc.concat([item]), []));

Reproduction

import { from, Observable } from "rxjs";
import { reduce } from "rxjs/operators";

function load(items: string[]): Observable<string[]> {
  return from(items).pipe(reduce((acc, item) => acc.concat([item]), []));
}

Expected behavior
It should not throw the typing error when trying to reduce.

Environment

  • Runtime: NodeJS 8.11
  • RxJS version: 6.3.1
  • Angular 6.2, but reproducible without as well.
@jgbpercy
Copy link

tl;dr: I'm pretty sure this isn't a bug, and I think your code was getting around typechecking due to a bug in rxjs 6.2.1. I believe you need to be explicit about the types passed to reduce, and the following should type check and work correctly under rxjs 6.3.x+:

function load(items: string[]): Observable<string[]> {
  return from(items).pipe(reduce((acc, item: string) => acc.concat([item]), []));
}

(change is item: string instead of just item)


Full explanation (disclaimer - I don't work on rxjs, I just enjoy figuring this stuff out and I could be wrong):

I believe your code was getting around correct typing using the funky pipe<R>(...operations: OperatorFunction<any, any>[]): Observable<R>; pipe overload added in #3789 and removed in #3945 .

If you take your code under rxjs 6.2.2 and change it to:

function load(items: string[]): Observable<string[]> {
  const thingToReturn = from(items).pipe(reduce((acc, item) => acc.concat([item]), []));
  return thingToReturn;
}

You'll notice there's an error, because thingToReturn is an Observable<{}> (similarly removing the explicit Observable<string> return type annotation changes the fn return type to Observable<{}>).

I think what's happening in 6.2.2 is something along the lines of:

  • Your current use of reduce looks to TS like reduce<any[]>(args): MonoTypeOperatorFunction<any[]> (under any rxjs version). If you mouse over reduce in your code you get reduce<any[]>..., and mouse over the item arg to see that it is similarly any[], when it should actually be string.
  • TS is doing some inference based on the combination of your expression being in the return statement, combined with the Observable<string> return type and the errant pipe overload, to decide that you are calling .pipe<string[]>(op). The bad overload makes TS not care what the actual type params were for your reduce.
  • Taking the expression out of the return breaks that funky inference on pipe's type param, TS goes for {} instead because it has nothing better to go on, and that is not compatible with string[], hence the error.

In 6.3 the bad overload is gone, the inference doesn't happen even with the expression in the return, and TS errors because your reduce has type param any[], but pipe wants an operator with input of string.

In this case you need to tell TS explicitly that you are not using reduce<any[]> and are instead using reduce<string, string[]>. You can do this like reduce((acc, item: string) => acc.concat([item]), []) or reduce<string, string[]>((acc, item) => acc.concat([item]), []).

I'm not sure if there's anything that can be done in reduce.ts to allow TS to better infer when the two-type-param overload is wanted without the usage having to be specific. I don't think there is, but I could easily be missing something!

@zsuswy
Copy link

zsuswy commented Jan 15, 2019

@jgbpercy
Great! This works for me. And your explanation is heuristic.

@cartant cartant added the TS Issues and PRs related purely to TypeScript issues label Jan 27, 2019
@benlesh
Copy link
Member

benlesh commented May 24, 2021

This has been addressed to the best of our ability in RxJS 7.0

@benlesh benlesh closed this as completed May 24, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
TS Issues and PRs related purely to TypeScript issues
Projects
None yet
Development

No branches or pull requests

5 participants