Search code examples
angulartypescriptngrxoftype

Writing a custom ngrx operator and returning the source observable type


I have a custom operator, waitFor which I am using in my effects as so:

public effect$: Observable<Action> = createEffect(() => {
    return this.actions$.pipe(
      ofType(myAction),
      waitFor<ReturnType<typeof myAction>>([anotherAction]),
      ...etc
    );
  });

It basically looks at correlationId's to not continue executing until the array of actions have been dispatched. But that is beside the point here.

As expected ofType takes the source observable and uses this as the return type, however I am struggling to achieve the same effect. As you can see above I am using ReturnType<typeof myAction>> and the following in my waitFor method:

export function waitFor<A extends Action>(actionsToWaitFor$: Array<Actions>): OperatorFunction<A, A> {

So at the moment if I call waitFor like this:

public effect$: Observable<Action> = createEffect(() => {
    return this.actions$.pipe(
      ofType(myAction),
      waitFor([anotherAction]),
      ...etc
    );
  });

Then its type is inferred as Action, but I want this to be ReturnType<typeof theSourceObservable> by default. So I would assume I would need something like this in my waitFor method:

export function waitFor<A extends ReturnType<typeof sourceObservable?!>>(actionsToWaitFor$: Array<Actions>): OperatorFunction<A, A> {

waitFor looks like this:

export function waitFor<A extends Action>(actionsToWaitFor$: Array<Actions>): OperatorFunction<A, A> {
  return (source$) => {
    return source$.pipe(
      switchMap((action: A & { correlationId: string}) => {
        // use zip() to wait for all actions 
        // and when omitting map((action) => action)
        // so the original action is always returned
      })
    );
  };
}

From the ofType source, it looks like I need to use Extract

Update

StackBlitz example is shown here


Solution

  • This, at least, compiles; I don't know if it also does what you need.

    public effect3$: Observable<Action> = createEffect(() => {
      const a:Action[]= []
    
      return this.actions$.pipe(
        ofType(doSomething),
        this.someCustomOperatorReturningStaticTypes(),
        this.thisWontWork(a),
        tap(({aCustomProperty}) => {
          // The type is inferred
          console.log(aCustomProperty);
        }),
      )
    });
    
    private thisWontWork<A extends Action>(actionsToWaitFor$: Action[]): OperatorFunction<A, A> {
      return (source$) => {
        return source$.pipe(
          tap(() => {
            console.log('Should work')
          })
        )
      }
    }
    

    I wasn't able to run it in the StackBlitz, any hint?

    Hope this helps