0

I have the following model :

export interface IModel<T> {
 exp: number,
 value: T
}

I want to create a custom RxJS operator such as :

private customOperator<U extends A | B, T extends IModel<U>>(custom: Observable<T>): Observable<U> {
    return custom.pipe(map((x: T) => x.value ));
  }

But I Have a type error when using it :

  mySub:  = new Subject<IModel<A>>;
  myObs$: Observable<A> = this.mySub.asObservable().pipe(this.customOperator); // <== ERROR

The error : Can't assign type 'Observable<A | B>' to type 'Observable< A>'.

Any idea on how I could change my custom operator to avoid the issue ?

1
  • Can you tell use-case for this ? As I know the correct answer is provided to this question. But I also want to know in which use-case this is generic operator function is useful to you. Thank you. Commented Dec 30, 2021 at 8:26

2 Answers 2

4

Wrap the operator in a factory function.

private customOperator<T extends IModel<U>, U extends A | B> () {
  return (custom: Observable<T>): Observable<U> =>
   custom.pipe(map((x: T) => x.value ));
}

And then in the pipe use the operator as a function call instead of the function ref.

myObs$: Observable<A> = this.mySub.asObservable().pipe(this.customOperator());

cheers

Sign up to request clarification or add additional context in comments.

1 Comment

pipe already returns an observable... asObservable here is redundant. 😊
2

You need to use the Typescript type assertion feature.

In your case the compiler complains because myObs$ must be of type Observable<A> while your custom operator can return either an Observable<A> or an Observable<B>.

So if you want to avoid the error you need to reassure Typescript that your custom operator will certainly return an Observable<A>. This is a trick to avoid a legitimate check of the Typescript compiler, so you better be sure you are doing the right thing.

So the code should look like this

myObs$: Observable<A> = this.mySub.asObservable().pipe(this.customOperator) as Observable<A>;

or, more simply

myObs$ = this.mySub.asObservable().pipe(this.customOperator) as Observable<A>;

If you hover over myObs$, for instance within VSCode, you will see that the type inferred for myObs$ is actually Observable<A> even if you have not specified the type (at least in this last version of the code)

UPDATE based on @akotech response

A better way to approach this case is what has been proposed by @akotech.

customOperator as coded below returns an Observable<U>

private customOperator<T extends IModel<U>, U extends A | B> () {
  return (custom: Observable<T>): Observable<U> =>
   custom.pipe(map((x: T) => x.value ));
}

Interestingly, at least in my case, if I do not declare the type of myObs$ like in this line

myObs$ = mySub.pipe(customOperator_())

the type inferred for myObs$ is Observable<A | B> but, at the same time, if I try something like this

myObs$: : Observable<B> = mySub.pipe(customOperator_())

I get an error from the compiler Type 'Observable<IModel>' is not assignable to type 'Observable<IModel>', which is correct since mySub notifies objects of type IModel<A>.

At the same time, if I try something like this

myObs$: : Observable<A> = mySub_A.pipe(customOperator_())

no error is raised by the compiler, which again is correct.

So the solution proposed by @akotech is safer than the one proposed by me.

1 Comment

this answer works fine, but the one suggested by @akotech seems les dangerous since i dont have to specify the type my self. Thanks a lot for your help !

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.