From f3f0679546088c8d5e0ba472041b87527a4c3d43 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Mon, 20 Jan 2020 17:29:21 -0600 Subject: [PATCH 1/2] fix(startWith): accepts N arguments and returns correct type - Improves documentation a bit - Updates type tests NOTE: Calls with more than 7 arguments that have a Scheduler at the end will return the wrong time. This is consider a corner case and low-risk BREAKING CHANGE: `startWith` will return incorrect types when called with more than 7 arguments and a scheduler. Passing scheduler to startWith is deprecated --- spec-dtslint/operators/startWith-spec.ts | 42 ++++++++--------- src/internal/operators/startWith.ts | 57 +++++++++++------------- 2 files changed, 44 insertions(+), 55 deletions(-) diff --git a/spec-dtslint/operators/startWith-spec.ts b/spec-dtslint/operators/startWith-spec.ts index 5b4a9f5322..a2c5ec69ec 100644 --- a/spec-dtslint/operators/startWith-spec.ts +++ b/spec-dtslint/operators/startWith-spec.ts @@ -1,30 +1,24 @@ import { of, asyncScheduler } from 'rxjs'; import { startWith } from 'rxjs/operators'; +import { a, b, c, d, e, f, g, h } from '../helpers'; -it('should infer correctly with one value', () => { - const o = of(1, 2, 3).pipe(startWith(4)); // $ExpectType Observable -}); - -it('should infer correctly with multiple values', () => { - const o = of(1, 2, 3).pipe(startWith(4, 5, 6)); // $ExpectType Observable -}); - -it('should infer correctly with no value', () => { - const o = of(1, 2, 3).pipe(startWith()); // $ExpectType Observable -}); - -it('should infer correctly with a value and a scheduler', () => { - const o = of(1, 2, 3).pipe(startWith(5, asyncScheduler)); // $ExpectType Observable -}); - -it('should infer correctly with a different type', () => { - const o = of(1, 2, 3).pipe(startWith('foo')); // $ExpectType Observable -}); - -it('should infer correctly with multiple different types', () => { - const o = of(1, 2, 3).pipe(startWith('foo', 4, true)); // $ExpectType Observable +it('should infer correctly with N values', () => { + const r0 = of(a).pipe(startWith()); // $ExpectType Observable + const r1 = of(a).pipe(startWith(b)); // $ExpectType Observable + const r2 = of(a).pipe(startWith(b, c)); // $ExpectType Observable + const r3 = of(a).pipe(startWith(b, c, d)); // $ExpectType Observable + const r4 = of(a).pipe(startWith(b, c, d, e)); // $ExpectType Observable + const r5 = of(a).pipe(startWith(b, c, d, e, f)); // $ExpectType Observable + const r6 = of(a).pipe(startWith(b, c, d, e, f, g)); // $ExpectType Observable + const r7 = of(a).pipe(startWith(b, c, d, e, f, g, h)); // $ExpectType Observable }); it('should infer correctly with only a scheduler', () => { - const o = of(1, 2, 3).pipe(startWith(asyncScheduler)); // $ExpectType Observable -}); + const r = of(a).pipe(startWith(asyncScheduler)); // $ExpectType Observable + const r1 = of(a).pipe(startWith(b, asyncScheduler)); // $ExpectType Observable + const r2 = of(a).pipe(startWith(b, c, asyncScheduler)); // $ExpectType Observable + const r3 = of(a).pipe(startWith(b, c, d, asyncScheduler)); // $ExpectType Observable + const r4 = of(a).pipe(startWith(b, c, d, e, asyncScheduler)); // $ExpectType Observable + const r5 = of(a).pipe(startWith(b, c, d, e, f, asyncScheduler)); // $ExpectType Observable + const r6 = of(a).pipe(startWith(b, c, d, e, f, g, asyncScheduler)); // $ExpectType Observable + }); diff --git a/src/internal/operators/startWith.ts b/src/internal/operators/startWith.ts index 81b495a38f..e2e3e45f8d 100644 --- a/src/internal/operators/startWith.ts +++ b/src/internal/operators/startWith.ts @@ -1,7 +1,7 @@ import { Observable } from '../Observable'; import { concat } from '../observable/concat'; import { isScheduler } from '../util/isScheduler'; -import { MonoTypeOperatorFunction, OperatorFunction, SchedulerLike } from '../types'; +import { MonoTypeOperatorFunction, OperatorFunction, SchedulerLike, ValueFromArray } from '../types'; /* tslint:disable:max-line-length */ /** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([[a, b, c], source], scheduler).pipe(concatAll())`) */ @@ -19,20 +19,14 @@ export function startWith(v1: D, v2: E, v3: F, v4: G, v5: H, s /** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([[a, b, c], source], scheduler).pipe(concatAll())`) */ export function startWith(v1: D, v2: E, v3: F, v4: G, v5: H, v6: I, scheduler: SchedulerLike): OperatorFunction; -export function startWith(v1: D): OperatorFunction; -export function startWith(v1: D, v2: E): OperatorFunction; -export function startWith(v1: D, v2: E, v3: F): OperatorFunction; -export function startWith(v1: D, v2: E, v3: F, v4: G): OperatorFunction; -export function startWith(v1: D, v2: E, v3: F, v4: G, v5: H): OperatorFunction; -export function startWith(v1: D, v2: E, v3: F, v4: G, v5: H, v6: I): OperatorFunction; -export function startWith(...array: D[]): OperatorFunction; -/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([[a, b, c], source], scheduler).pipe(concatAll())`) */ -export function startWith(...array: Array): OperatorFunction; -/* tslint:enable:max-line-length */ +export function startWith(...values: A): OperatorFunction>; /** - * Returns an Observable that emits the items you specify as arguments before it begins to emit - * items emitted by the source Observable. + * Returns an observable that, at the moment of subscription, will synchronously emit all + * values provided to this operator, then subscribe to the source and mirror all of its emissions + * to subscribers. + * + * This is a useful way to know when subscription has occurred on an existing observable. * * First emits its arguments in order, and then any * emissions from the source. @@ -41,36 +35,37 @@ export function startWith(...array: Array): Operato * * ## Examples * - * Start the chain of emissions with `"first"`, `"second"` + * Emit a value when a timer starts. * * ```ts - * import { of } from 'rxjs'; + * import { timer } from 'rxjs'; * import { startWith } from 'rxjs/operators'; * - * of("from source") - * .pipe(startWith("first", "second")) + * timer(1000) + * .pipe( + * map(() => 'timer emit'), + * startWith('timer start') + * ) * .subscribe(x => console.log(x)); * * // results: - * // "first" - * // "second" - * // "from source" + * // "timer start" + * // "timer emit" * ``` * - * @param {...T} values - Items you want the modified Observable to emit first. - * @param {SchedulerLike} [scheduler] - A {@link SchedulerLike} to use for scheduling - * the emissions of the `next` notifications. - * @return {Observable} An Observable that emits the items in the specified Iterable and then emits the items - * emitted by the source Observable. - * @name startWith + * @param values Items you want the modified Observable to emit first. + * + * @see endWith + * @see finalize + * @see concat */ -export function startWith(...array: Array): OperatorFunction { - const scheduler = array[array.length - 1] as SchedulerLike; +export function startWith(...values: D[]): OperatorFunction { + const scheduler = values[values.length - 1]; if (isScheduler(scheduler)) { // deprecated path - array.pop(); - return (source: Observable) => concat(array as D[], source, scheduler); + values.pop(); + return (source: Observable) => concat(values, source, scheduler); } else { - return (source: Observable) => concat(array as D[], source); + return (source: Observable) => concat(values, source); } } From a6b9ab8d303b0d8573aad700ee00e59ee667d0d3 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Mon, 20 Jan 2020 17:39:51 -0600 Subject: [PATCH 2/2] docs(startWith): fix example imports --- src/internal/operators/startWith.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal/operators/startWith.ts b/src/internal/operators/startWith.ts index e2e3e45f8d..fa0e9c44dc 100644 --- a/src/internal/operators/startWith.ts +++ b/src/internal/operators/startWith.ts @@ -39,7 +39,7 @@ export function startWith(...values: A): OperatorFunction