Description
Given the move away from lift: #7201 #7202
We can make our observable even more compatible with other observables by migrating us away from the pipe
method. This is not something that would happen immediately, rather over a very, very long period. The idea is to increase compatibility and improve ergonomics.
Problem: It's annoying to have to from(x)
types in order to use our operators with them
There's a common issue where people want to convert other things to observables in order to use our operators. Whether it's an array or a promise, there are times were you need to do from
and the only reason you're doing it is because you want that .pipe
method, and you want an observable to operate on.
Problem: pipe
is a method
- It's not tree shakable
- It's unlikely to exist on "other observables".
The second one is more of a problem, honestly. If people implement code with the expectation that pipe
exists as a method on whatever observable they get, then even while we've made sure our operators will work with any "same-shaped" observable, that custom code will break, because it's expecting pipe
to be a method.
Proposal
A new rx
method that accepts an ObservableInput
as the first argument, and a rest of operators:
export function rx<A, B>(source: ObservableInput<A>, a: OperatorFunction<A, B>): Observable<B>;
export function rx<A, B, C>(source: ObservableInput<A>, a: OperatorFunction<A, B>, b: OperatorFunction<B, C>): Observable<C>;
export function rx<A, B, C, D>(source: ObservableInput<A>, a: OperatorFunction<A, B>, b: OperatorFunction<B, C>, c: OperatorFunction<C, D>): Observable<D>;
// ... and so on
export function rx<T>(source: ObservableInput<T>, ...operators: OperatorFunction<T, any>[]): Observable<unknown> {
returm pipeArray(operators)(from(source))
}
(Technically, rx
is just a standard functional pipe with some specific types, it could be the same pipe
method we expose under the hood)
The usage would be as such:
import { rx, map, delay } from 'rxjs'
rx(source$, map(n => n + n)).subscribe(console.log)
// or
rx(somePromise, delay(1000)).subscribe(console.log)
// or
rx(someAsyncIterable, map(n => n + n)).subscribe(console.log)
// ...and so on.
Bonus? from
could migrate to just be rx
as well
For a long time people have complained about from
being a horrible name for an exported function. (Although I do like import { from } from './from'
in our codebase 😄). This would mean that couple could technically just do: rx(somePromise)
and it's the same as from(somePromise)
.