Skip to content

Operator functions are acceptable to concat() function in Typescript. #4563

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

Closed
Hardtack opened this issue Feb 13, 2019 · 5 comments
Closed

Comments

@Hardtack
Copy link

Hardtack commented Feb 13, 2019

Bug Report

Current Behavior
rxjs is not able to subscribe general functions like () => {}. So, the following code must make errors.

import { from } from "rxjs";
const fn = (a: number) => {};
from(fn);

Of course, It makes an error on 3rd line, but if we add a slight of type hints, it makes things confuse.

import { from } from "rxjs";
const fn: ArrayLike<any> = (a: number) => {};
from(fn);

Now there's no error!

Every functions are instance of ArrayLike<any> and where ArrayLike is defined as

interface ArrayLike<T> {
    readonly length: number;
    readonly [n: number]: T;
}

It doesn't matter, if we don't add an explicit type hint for being a function to ArrayLike.
But there are some problem originated from this issue.

Reproduction

I meant to write codes like following

function loadStringFromRemote(name: string): Observable<string> {
    return of("Hello, " + name + "!");
}

function observableFactory(): Observable<string> {
    return concat(
        flatMap(() => loadStringFromRemote("World").pipe(
            catchError((error) => of("Error!")),
        )),
    );
}

But, by mistake, I actually wrote the code like

function loadStringFromRemote(name: string): Observable<string> {
    return of("Hello, " + name + "!");
}

function observableFactory(): Observable<string> {
    return concat(
        flatMap(() => loadStringFromRemote("World")),
        catchError((error) => of("Error!")),
    );
}

But it still compiled without any type errors!

I didn't notice my mistake, and tried to subscribe an observable from the factory function, and it showed an errors at runtime.

Uncaught TypeError: You provided 'function catchErrorOperatorFunction(source) {
        var operator = new CatchOperator(selector);
        var caught = source.lift(operator);
        return (operator.caught = caught);
    }' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
    at subscribeTo (_app.js:90050)
    at subscribeToResult (_app.js:90207)

It happened since concat() has following type signature

export declare function concat<O1 extends ObservableInput<any>>(v1: O1, scheduler?: SchedulerLike): Observable<ObservedValueOf<O1>>;
export declare function concat<O1 extends ObservableInput<any>, O2 extends ObservableInput<any>>(v1: O1, v2: O2, scheduler?: SchedulerLike): Observable<ObservedValueOf<O1> | ObservedValueOf<O2>>;
export declare function concat<O1 extends ObservableInput<any>, O2 extends ObservableInput<any>, O3 extends ObservableInput<any>>(v1: O1, v2: O2, v3: O3, scheduler?: SchedulerLike): Observable<ObservedValueOf<O1> | ObservedValueOf<O2> | ObservedValueOf<O3>>;
export declare function concat<O1 extends ObservableInput<any>, O2 extends ObservableInput<any>, O3 extends ObservableInput<any>, O4 extends ObservableInput<any>>(v1: O1, v2: O2, v3: O3, v4: O4, scheduler?: SchedulerLike): Observable<ObservedValueOf<O1> | ObservedValueOf<O2> | ObservedValueOf<O3> | ObservedValueOf<O4>>;
export declare function concat<O1 extends ObservableInput<any>, O2 extends ObservableInput<any>, O3 extends ObservableInput<any>, O4 extends ObservableInput<any>, O5 extends ObservableInput<any>>(v1: O1, v2: O2, v3: O3, v4: O4, v5: O5, scheduler?: SchedulerLike): Observable<ObservedValueOf<O1> | ObservedValueOf<O2> | ObservedValueOf<O3> | ObservedValueOf<O4> | ObservedValueOf<O5>>;
export declare function concat<O1 extends ObservableInput<any>, O2 extends ObservableInput<any>, O3 extends ObservableInput<any>, O4 extends ObservableInput<any>, O5 extends ObservableInput<any>, O6 extends ObservableInput<any>>(v1: O1, v2: O2, v3: O3, v4: O4, v5: O5, v6: O6, scheduler?: SchedulerLike): Observable<ObservedValueOf<O1> | ObservedValueOf<O2> | ObservedValueOf<O3> | ObservedValueOf<O4> | ObservedValueOf<O5> | ObservedValueOf<O6>>;
export declare function concat<O extends ObservableInput<any>>(...observables: (O | SchedulerLike)[]): Observable<ObservedValueOf<O>>;
export declare function concat<R>(...observables: (ObservableInput<any> | SchedulerLike)[]): Observable<R>;

The return type of catch error is OperatorFunction<T, T | R> and it is converted like following.

const foo: OperatorFunction<string, string> = catchError((error) => of("Error!"));
const bar: UnaryFunction<Observable<string>, Observable<string>> = foo;
const baz: ArrayLike<any> = bar;
const qux: ObservableInput<any> = baz;
concat(qux);
// is equivalent to
concat(catchError((error) => of("Error!")))

Expected behavior
It have to make errors before 5th line.

Environment

  • Runtime: Typescript 3.2.2
  • RxJS version: 6.4

I spent a lot of time to debug my problem. It would be helpful to the people who are suffering the problem like mine.

@cartant
Copy link
Collaborator

cartant commented Feb 13, 2019

The signatures you have included in your issue are for the (deprecated) operator, but you appear to be using the observable creator in your snippets.

@Hardtack
Copy link
Author

I found the signatures from the code of observable creator, which was modified in this commit
I'm using rxjs 6.3.3 but it was changed in 6.4.0 which has same problem

I'll update the issue with new type signatures. Sorry for confusing.

@benlesh
Copy link
Member

benlesh commented Feb 9, 2021

This issue is no longer a real problem, as newer versions of RxJS and TypeScript will not allow this. However, I've started a new issue #6011, because ArrayLike is still weird and doesn't behave like we'd want it to.

@cartant
Copy link
Collaborator

cartant commented Mar 13, 2021

Closing because of #4563 (comment)

@cartant cartant closed this as completed Mar 13, 2021
@cartant
Copy link
Collaborator

cartant commented Mar 14, 2021

Also, this should be resolved when I get around to replacing the ObservableInput<any> types with ObservableInput<unknown> in the concat signatures. See the changes in this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants