Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How RXJS type inference work when multiple pipe is used typescript

const of = require('rxjs').of;
const map = require('rxjs/operators').map

of(123).pipe(
  map(num => num.toString()),
  map(str => str.substring(0,1)),
).subscribe(console.log);

In the second map above, the type of str parameter is correctly inferred from the previous map that returns a string. I am curious how typescript is inferring the type in the second map operator.

Is this RxJS who nicely designed the code so that it can happen?
Or is it just VS code that has special IntelliSense for RxJS?

like image 983
Mohayemin Avatar asked Apr 16 '26 16:04

Mohayemin


1 Answers

I'd say it is all handled by RxJS.

Looking at the pipe overloads

pipe(): Observable<T>;
  pipe<A>(op1: OperatorFunction<T, A>): Observable<A>;
  pipe<A, B>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>): Observable<B>;
  pipe<A, B, C>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>): Observable<C>;
...

we can see that there's a lot going on.

OperatorFunction<T, A> type refers to a function whose single parameter is an Observable of type T and whose return type is also an Observable of type A:

export interface OperatorFunction<T, R> extends UnaryFunction<Observable<T>, Observable<R>> {}

Let's also have a look at map's signature

export function map<T, R>(project: (value: T, index: number) => R, thisArg?: any): OperatorFunction<T, R> { ... }

As you can see, its return type will be a function that receives an observable and returns another observable. In this case, the type of the returned Observable(R) is inferred from the provided projection function: (value: T, index: number) => R

So, if you have

const src$ = of(1).pipe(map(v => '' + v));

src$ will be an observable whose type T will be string.

And here's why type inference also works inside subscribe:

subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): Subscription; // Providing callbacks

subscribe(observer?: PartialObserver<T>): Subscription; // Providing an observer object

where T is the inferred type of the Observable(i.e from map's provided fn in this case).

like image 127
Andrei Gătej Avatar answered Apr 19 '26 05:04

Andrei Gătej