From 82e3ffc2b9fe3a35a5cedec535aae9a90545ed17 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Mon, 20 Jan 2020 17:29:21 -0600 Subject: [PATCH] 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 | 58 +++++++++++------------- 2 files changed, 44 insertions(+), 56 deletions(-) diff --git a/spec-dtslint/operators/startWith-spec.ts b/spec-dtslint/operators/startWith-spec.ts index 5b4a9f53220..a2c5ec69ec4 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 81ce063213b..9d085f1e557 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,37 +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. - * @method startWith - * @owner Observable + * @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: Array): OperatorFunction { + const scheduler = values[values.length - 1] as SchedulerLike; if (isScheduler(scheduler)) { // deprecated path - array.pop(); - return (source: Observable) => concat(array as T[], source, scheduler); + values.pop(); + return (source: Observable) => concat(values as T[], source, scheduler); } else { - return (source: Observable) => concat(array as T[], source); + return (source: Observable) => concat(values as T[], source); } }