Current Behavior
After #3945, the following is now a type error:
of(1, 2, 3).pipe(...[map((x) => x)]);
error TS2557: Expected at least 0 arguments, but got 0 or more.
Arguably, this is TS error as witnessed by the odd error msg.
Possible Solution
Not sure if it is possible to have the good behaviors #3945 introduced in terms of error reporting and passing spread args.
cc @cartant
@rkirov I've been playing around with a simple harness that uses overloads and rest parameters. With TypeScript 3.0, you can use tuple parameter extraction to make it work:
type Parameters<T> = T extends (...args: infer U) => any ? U : never;
function f(): {};
function f<A>(a: A): {};
function f<A>(a: A, ...rest: any[]): {};
function f(...rest: any[]): {} {
return {};
}
const pf: Parameters<typeof f> = [1, 2, 3];
const rf = f(...pf); // OK
Note that the pf variable that's spread needs to have the appropriate tuple type. Otherwise, the error you mentioned is effected:
const pf = [1, 2, 3];
const rf = f(...pf); // error TS2557: Expected at least 0 arguments, but got 0 or more.
With pipe, because the inclusion of a signature with a single rest parameter matches operators with types that would otherwise be deemed incompatible, I don't think one can be reinstated.
Perhaps exposing the pipeFromArray function - or some variant of it - would be a suitable workaround?
Also, I've just noticed that the type declarations for the static pipe and pipeFromArray are incorrect and need some attention.
It's also worth noting that spreading would have only worked in 6.2.1 and 6.2.2 - after #3789 was merged. And the change in that PR circumvented the type checking performed by the other signatures.
In releases prior to that - including v5 - the catch-all signature was typed in such away that spreading would not have worked in many circumstances:
pipe<R>(...operations: OperatorFunction<T, R>[]): Observable<R>
Anyone knows a workaround to this issue?
@MatissJanis See the two comments immediately above yours. One has the workaround and one explains that this only worked in certain versions of RxJS and introduced other - more significant - problems.
Sadly I can't use Typescript v3, but only v2. Do you know any solution for an older Typescript version?
@MatissJanis Unfortunately, the pipeFromArray utility is internal. It's what you really need. Without it you could cook up your own or use reduce, like this:
const ops = [map((x) => x)];
const result = ops.reduce((ob: Observable<{}>, op: OperatorFunction<{}, {}>) => ob.pipe(op), of(1, 2, 3));
result.subscribe(value => console.log(value));
It ain't pretty, but you could wrap that in a function to impose a particular result type, etc.
The bottom line is that the spread syntax ain't gonna work with the current overloads (without TypeScript 3.0's tuple parameter extraction) and those with which it did work introduced problems that were far worse.
Got it. Thanks for the fast replies @cartant!
Ot dushi, @cartant!
+1
Faced the issue as well while upgrading to ng7 (thus moving to typescript 3).
Thanks to @cartant, the _pipeFromArray_ workaround worked great for me.
In case there are anyone in the same situation, here is a simplified snippet of the workaround (because code speak better than words 馃槂 ):
// Before
req.pipe(...operators);
// After
pipeFromArray([...operators])(req);
+1
I'm using _pipeFromArray_. It works
This is my code. I'm using a common response handler for http call.
pipeFromArray(EntityManagerService.commonHandler)(this.http.get( ___ params___ ));
private static commonHandler: OperatorFunction<any, any>[] =
[retry(3), catchError(EntityManagerService.handleError)];
you could also use the apply function:
before:
.pipe(...selectors)
after:
.pipe.apply(context, selectors)
Most helpful comment
you could also use the apply function:
before:
.pipe(...selectors)after:
.pipe.apply(context, selectors)