Increasing the number of overloads for pipe()? #6381
Replies: 2 comments
-
Although it can be annoying sometimes, I'm not fully convinced it's necessary tbh. Specially because it's easily fixable: something$.pipe(
A(),
B(),
// ...
H(),
I(),
J(),
K()
); is equivalent to: something$.pipe(
A(),
B(),
// ...
H()
).pipe(
I(),
J(),
K()
); I usually don't make longer pipes than 5 operators.... by that point I usually have a meaningful name I can give to that stream so I can keep composing it down. Do you already have a pipe with 8 operators and you want to debug it by adding 3-4 In a way I like having a soft limit at 9, because this way if I ever reach it, I get reminded that probably I need to break that stream into smaller bits, which does help in readability. |
Beta Was this translation helpful? Give feedback.
-
Yes, I've done this on occasion. I don't really like doing it as it is a bit jarring to have the extra pipes in there, when it shouldn't really be necessary. A 9-operator limit is pretty small. I try to write code that "tells a story" so it is very clear, and with higher level code, composing descriptive operators sequentially makes for really nice code, and it would be great to be able to compose more operators in a single pipe without having to break it down for no other reason than an arbitrary limit. An example of a custom operator that composes several other validation operators: export const verifyRequest = (
authorityId: number,
cardNumber: string,
): UnaryFunction<Observable<Authority>, Observable<Authority>> =>
pipe(
tap(measureTiming()),
verifyAuthorityExists(),
verifyCardNumberForAuthority(authorityId),
verifyAuthorityIsActive(),
verifyBinCodeIsEnabled(cardNumber),
verifyIntegrationSpecified(cardNumber),
tap(reportTimingObserver('verifyRequest')),
); A later method that composes the above validation operator, with other operators, to perform a specific higher level workflow: this.authorities.loadAll().pipe(
tap(measureTiming()),
findAuthority(cardNumber),
verifyRequest(authorityId, cardNumber),
resolveIntegration(this.integrationResolver.knownIntegrations, cardNumber),
queryBenefits({
cardNumber,
householdId,
benefitsCriteria: query,
includeOriginal,
}),
normalizeBenefits(),
sortEmptyBenefitsLast(),
logError(),
tap(reportTimingObserver('resolve')),
); A mere 9 operators is a very arbitrary and tight limit. I already do what I can to break things down. I aim to follow SRP as much as I can. The If I needed to debug the above stream...which in fact I did, and what lead to creating this request...I already had 9 operators. The moment I added a tap for logging, the typing fell apart. I feel very strongly that we could benefit from more I'll also offer that, when adding taps for debugging purposes, they are often temporary. This makes the whole chained pipes option far less than ideal, as it means temporarily breaking up the pipe just so you can add temporary taps for logging. So you have to change the code more than necessary to solve runtime problems in certain environments. When you resolve the issue and remove the taps, you would then have to merge multiple pipes back into one. Its all very unnecessary, if we could get a pipe that supports more than just 9 operators. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I am a huge fan of RxJs, and have been using it heavily for a number of years now as a basis for purely functionally reactive programming in JavaScript. By that, I mean I no longer write imperative code, I pretty much write functional code (with occasional objects where methods are also implemented with RxJs, and in a functional manner). I've started making pretty heavy use of pipes...both the
Observable.prototype.pipe()
and the unbound, freepipe()
function. The latter I use as my primary means of implementing custom operators, which are then composed into Observable pipes.One of the challenges I have run into is the 9-operator limit of the existing pipe overloads...both on the
Observable.prototype.pipe()
and the unboundpipe()
function. As soon as you surpass 9 operators, you suddenly lose typing, as the 10th overload's return type isUnaryFunction<T, unknown>
, so this overload is generally unusable, and TypeScript always freaks out:I have two cases where sometimes I need more than 9 operators in a pipe:
Most of the time, its the logging scenario, where I may need to temporarily add some
tap
s between operators to figure out what's going on, and that will frequently bust the 9-operator limit. I am also finding more value in doing basic performance timing using custom operators...one operator placed at the beginning of a sequence of operators to start timing, and another placed at the end to report the timing. This is often combined withtap
s for other logging. In any case, because the moment we hit thatunknown
return type once we surpass 9 operators, it can make some debugging in certain circumstances quite challenging.The other case that is becoming more common, as I rely more and more on custom operators to encapsulate business behavior, are my higher level Observable pipes. Sometimes I may compose 10+ operators, and in those cases to get around the 9-overload limit, I am creating additional operators simply to keep my total operator limit under 10, without any other real design value.
I'm wondering if we could increase the number of strongly typed overloads of the
pipe()
function and theObservable.prototype.pipe()
method. Could we expand it to 20 operators, or so? That would make temporary logging withtap
s much, much easier, especially if you already have 9 operators and need to tap between each to track the state of your value as it moves through the pipe, you can hit 20 operators easy.Kind Regards!
Beta Was this translation helpful? Give feedback.
All reactions