8000 feat(op): Adds the new .op() method to Observable · ReactiveX/rxjs@28538de · GitHub
[go: up one dir, main page]

Skip to content

Commit 28538de

Browse files
committed
feat(op): Adds the new .op() method to Observable
Allows you to apply an operator to a given stream, without it needing to be on the Observable prototype. This is useful whenever you don't want to unintentionally leak implementation details or otherwise rely on the fact that you're using a particular operator. It takes the provided operator function and any arbitrary arguments then internally just calls `operator.apply(this, args)`. This is solves a similar problem as the TC39 proposed [bind operator syntax](https://github.com/tc39/proposal-bind-operator) e.g. `stream::map(i => i + 1)` but that's stage-0 and not supported by TypeScript. Example 1: in your unit tests, if you were to patch additional operators onto the Observable prototype then the application you're testing could unknowingly be relying on those operators without it importing them itself. Your tests would pass because the operator is available in your tests, but when you run the app standalone it errors because the operator wasn't imported! Example 2: you're writing a library that itself uses RxJS. If you patch operators onto the prototype, then consumers of your library could accidentally depend on the fact that the operators you use are available.
1 parent 6ce4773 commit 28538de

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

spec/Observable-spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,3 +831,22 @@ describe('Observable.lift', () => {
831831
});
832832
});
833833
});
834+
835+
/** @test {Observable} */
836+
describe('Observable.operate', () => {
837+
it('should apply the provided operator and args to the given stream', () => {
838+
const { map, filter } = Rx.Observable.prototype;
839+
const e1 = cold('-a-b-c-|', { a: 1, b: 2, c: 3 });
840+
const expected = '---b-c-|';
841+
842+
// .o() is an alias for .operate()
843+
const result = e1
844+
.operate(filter, i => i > 1)
845+
.o(map, i => i * 10);
846+
847+
expectObservable(result).toBe(expected, {
848+
b: 20,
849+
c: 30
850+
});
851+
});
852+
});

src/Observable.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,52 @@ export class Observable<T> implements Subscribable<T> {
7171
return observable;
7272
}
7373

74+
/**
75+
* Allows you to apply an operator to a given stream, without it needing to be
76+
* on the Observable prototype. This is useful whenever you don't want to
77+
* unintentionally leak implementation details or otherwise rely on the fact
78+
* that you're using a particular operator.
79+
*
80+
* It takes the provided operator function and any arbitrary arguments
81+
* then internally just calls `operator.apply(this, args)`.
82+
*
83+
* This is solves a similar problem as the TC39 proposed [bind operator syntax](https://github.com/tc39/proposal-bind-operator)
84+
* e.g. `stream::map(i => i + 1)` but that's stage-0 and not supported by
85+
* TypeScript.
86+
*
87+
* Example 1: in your unit tests, if you were to patch additional operators
88+
* onto the Observable prototype then the application you're testing could
89+
* unknowingly be relying on those operators without it importing them itself.
90+
* Your tests would pass because the operator is available in your tests, but
91+
* when you run the app standalone it errors because the operator wasn't
92+
* imported!
93+
*
94+
* Example 2: you're writing a library that itself uses RxJS. If you patch
95+
* operators onto the prototype, then consumers of your library could
96+
* accidentally depend on the fact that the operators you use are available.
97+
*
98+
* @example <caption>Apply several operators without patching the Observable prototype</caption>
99+
* import { of } from 'rxjs/observable/of';
100+
* import { filter } from 'rxjs/operator/filter';
101+
* import { map } from 'rxjs/operator/map';
102+
*
103+
* of(1, 2, 3)
104+
* .op(filter, i => i > 1)
105+
* .op(map, i => i * 10)
106+
* .subscribe(x => console.log(x));
107+
* // 20..30
108+
*
109+
* @method op
110+
* @param {Function} operator The operator function you wish to apply.
111+
* @param {...any} args (optional) A spread of any arguments you want to apply
112+
* to the operator when we apply it to your stream.
113+
* @return {Observable<T>} An Observable that comes from the result of applying
114+
* the provided operator to the source.
115+
*/
116+
op<T>(operator: Function, ...args: Array<any>): Observable<T> {
117+
return operator.apply(this, args);
118+
}
119+
74120
/**
75121
* Registers handlers for handling emitted values, error and completions from the observable, and
76122
* executes the observable's subscriber function, which will take action to set up the underlying data stream

0 commit comments

Comments
 (0)
0