Skip to content

Commit

Permalink
feat(switchScan): add switchScan() operator (#4442)
Browse files Browse the repository at this point in the history
* feat(switchScan): add switchScan operator

* refactor(switchScan): Make switchScan behavior more close to `scan()`

Closes #2931

* docs(switchScan): add link switchScan marble diagram into its docblock

* test(switchScan): revert running only switchScan spec

* fix(switchScan): fix overriding the same seed for multiple observers

* docs(switchScan): fix typo

* refactor(switchScan): make seed mandatory as in mergeScan

* fix(switchScan): fix typings for union types, remove duplicate dts test

* test(switchScan): fix types after rebase to master

* refactor(switchScan): use `operate` and `switchMap`.

* chore: update golden files

* chore: address comments

Co-authored-by: Ben Lesh <[email protected]>
  • Loading branch information
martinsik and benlesh authored Oct 5, 2020
1 parent f23a2a8 commit 73fa910
Show file tree
Hide file tree
Showing 10 changed files with 499 additions and 0 deletions.
2 changes: 2 additions & 0 deletions api_guard/dist/types/operators/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@ export declare function switchMapTo<R>(observable: ObservableInput<R>): Operator
export declare function switchMapTo<R>(observable: ObservableInput<R>, resultSelector: undefined): OperatorFunction<any, R>;
export declare function switchMapTo<T, I, R>(observable: ObservableInput<I>, resultSelector: (outerValue: T, innerValue: I, outerIndex: number, innerIndex: number) => R): OperatorFunction<T, R>;

export declare function switchScan<T, R, O extends ObservableInput<any>>(accumulator: (acc: R, value: T, index: number) => O, seed: R): OperatorFunction<T, ObservedValueOf<O>>;

export declare function take<T>(count: number): MonoTypeOperatorFunction<T>;

export declare function takeLast<T>(count: number): MonoTypeOperatorFunction<T>;
Expand Down
1 change: 1 addition & 0 deletions docs_app/content/guide/operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ These are Observable creation operators that also have join functionality -- emi
- [`partition`](/api/operators/partition)
- [`pluck`](/api/operators/pluck)
- [`scan`](/api/operators/scan)
- [`switchScan`](/api/operators/switchScan)
- [`switchMap`](/api/operators/switchMap)
- [`switchMapTo`](/api/operators/switchMapTo)
- [`window`](/api/operators/window)
Expand Down
3 changes: 3 additions & 0 deletions docs_app/tools/decision-tree-generator/src/tree.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@
- label: and output the computed values as a nested Observable when the source emits a value
children:
- label: mergeScan
- label: and output the computed values as a nested Observable when the source emits a value while unsubscribing from the previous nested Observable
children:
- label: switchScan
- label: I want to wrap its messages with metadata
children:
- label: that describes each notification (next, error, or complete)
Expand Down
38 changes: 38 additions & 0 deletions spec-dtslint/operators/switchScan-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { of } from 'rxjs';
import { switchScan } from 'rxjs/operators';

it('should infer correctly', () => {
const o = of(1, 2, 3).pipe(switchScan((acc: boolean, v: number) => of(Boolean(v)), false)); // $ExpectType Observable<boolean>
});

it('should infer correctly when using a single type', () => {
const o = of(1, 2, 3).pipe(switchScan((acc, v) => of(acc + v), 0)); // $ExpectType Observable<number>
});

it('should infer correctly when using seed of a different type', () => {
const o = of(1, 2, 3).pipe(switchScan((acc, v) => of(acc + v), '0')); // $ExpectType Observable<string>
});

it('should support a projector that takes an index', () => {
const o = of(1, 2, 3).pipe(switchScan((acc, v, index) => of(Boolean(v)), false)); // $ExpectType Observable<boolean>
});

it('should support projecting to union types', () => {
const o = of(Math.random()).pipe(switchScan(n => n > 0.5 ? of(123) : of('test'), 0)); // $ExpectType Observable<string | number>
});

it('should use the inferred accumulator return type over the seed type', () => {
const o = of(1, 2, 3).pipe(switchScan(p => of(1), [])); // $ExpectType Observable<number>
});

it('should enforce types', () => {
const o = of(1, 2, 3).pipe(switchScan()); // $ExpectError
});

it('should enforce the return type to be Observable', () => {
const o = of(1, 2, 3).pipe(switchScan(p => p)); // $ExpectError
});

it('should enforce seed and accumulator to have the same type', () => {
const o = of(1, 2, 3).pipe(switchScan((acc, p) => of([...acc, p]))); // $ExpectError
});
1 change: 1 addition & 0 deletions spec/operators/index-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ describe('operators/index', () => {
expect(index.skipWhile).to.exist;
expect(index.startWith).to.exist;
expect(index.switchAll).to.exist;
expect(index.switchScan).to.exist;
expect(index.switchMap).to.exist;
expect(index.switchMapTo).to.exist;
expect(index.take).to.exist;
Expand Down
Loading

0 comments on commit 73fa910

Please sign in to comment.