ReactiveX / ReactiveX/rxjs

RFC: A new way of "piping"

Open
#7,203 39 comments 70 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

8.x 9.x AGENDA ITEM
Dominant language
TypeScript
Stars
31.7k
Forks
3k
PR merge metrics
No merged PRs in 30d

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

  1. It's not tree shakable
  2. 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).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the rx, pipe, and from APIs described in the issue, then review the ObservableInput and OperatorFunction types and the pipeArray implementation it mentions. Compare the proposed overloads and usage examples with the current public exports. Done would require an agreed migration design and compatibility plan; this RFC does not identify a file or concrete implementation task.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, developer-experience
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.