Search code examples
angulartypescriptrxjssubscription

How to do something in Rx subscription only after valid value?


How to do something in Rx subscription only after valid value?

For example, I receive 'null', 'null' and only after this array with data

For example:

this.someStream$.subscribe((data: any[]) => {
  if (data) {
    this.someVar= data.map(item => new SomeModel(item ));
});

How to avoid "if (data) {}"


Solution

  • As suggested, the filter pipe does the job, but you explicitely don't want to have a conditional, which can be understood from a verbosity perspective. This is not very pretty, right?

    filter(data => data !== undefined)
    filter(data => !!data)
    

    There are two solutions to avoid the !== or the shorthand !!data.

    First, you can make use of the Boolean constructor. It is a function with signature (any) => boolean, that's why you can do this:

    filter(Boolean)
    

    Be careful though, als falsy values (false, "", 0) will also be filtered that way. It's only save to use with object.

    Even better and more reusable is creating an own operator, that would look like this:

    function filterEmpty<T>(): MonoTypeOperatorFunction<T> {
        return (source: Observable<T>) => source.pipe(filter(val => val != undefined));
    }
    

    Then, you can use it with your code example:

    this.someStream$
        .pipe(filterEmpty())
        .subscribe((data: any[]) => {
            this.someVar = data.map(item => new SomeModel(item ));
        });
    

    TYPES

    There is another concern with this approach, and that is types.

    Imagine this:

    class X {
        public a;
    }
    
    let x: X | undefined = undefined;
    
    of(x)
        .pipe(filter(y => !!y))
        .subscribe(y => {
            let i = y.a;
    });
    

    We will get a type error when assigning i, as y is X | undefined. Very unfortunate.

    But with this typed operator, we can convert X | undefined into X:

    export function filterNullable<T>(): OperatorFunction<T | undefined, T> {
        return (source: Observable<T | undefined>) => source.pipe(
            filter<T | undefined, T>(
                function (val: T | undefined): val is T {
                    return val !== undefined;
                }));
    }
    

    Very handy.