Skip to content

fix(endWith): will properly type N arguments #5246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 13 additions & 37 deletions spec-dtslint/operators/endWith-spec.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,18 @@
import { of, asyncScheduler } from 'rxjs';
import { endWith } from 'rxjs/operators';
import { a, b, c, d, e, f, g, h } from '../helpers';

it('should support a scheduler', () => {
const a = of(1, 2, 3).pipe(endWith(asyncScheduler)); // $ExpectType Observable<number>
});

it('should infer type for 1 parameter', () => {
const a = of(1, 2, 3).pipe(endWith(4)); // $ExpectType Observable<number>
});

it('should infer type for 2 parameter', () => {
const a = of(1, 2, 3).pipe(endWith(4, 5)); // $ExpectType Observable<number>
});

it('should infer type for 3 parameter', () => {
const a = of(1, 2, 3).pipe(endWith(4, 5, 6)); // $ExpectType Observable<number>
});

it('should infer type for 4 parameter', () => {
const a = of(1, 2, 3).pipe(endWith(4, 5, 6, 7)); // $ExpectType Observable<number>
});

it('should infer type for 5 parameter', () => {
const a = of(1, 2, 3).pipe(endWith(4, 5, 6, 7, 8)); // $ExpectType Observable<number>
});

it('should infer type for 6 parameter', () => {
const a = of(1, 2, 3).pipe(endWith(4, 5, 6, 7, 8, 9)); // $ExpectType Observable<number>
});

it('should infer type for rest parameters', () => {
const a = of(1, 2, 3).pipe(endWith(4, 5, 6, 7, 8, 9, 10)); // $ExpectType Observable<number>
});

it('should infer with different types', () => {
const a = of(1, 2, 3).pipe(endWith('4', true)); // $ExpectType Observable<string | number | boolean>
});

it('should accept empty parameter', () => {
const a = of(1, 2, 3).pipe(endWith()); // $ExpectType Observable<number>
const r = of(a).pipe(endWith(asyncScheduler)); // $ExpectType Observable<A>
});

it('should infer type for N values', () => {
const r0 = of(a).pipe(endWith()); // $ExpectType Observable<A>
const r1 = of(a).pipe(endWith(b)); // $ExpectType Observable<A | B>
const r2 = of(a).pipe(endWith(b, c)); // $ExpectType Observable<A | B | C>
const r3 = of(a).pipe(endWith(b, c, d)); // $ExpectType Observable<A | B | C | D>
const r4 = of(a).pipe(endWith(b, c, d, e)); // $ExpectType Observable<A | B | C | D | E>
const r5 = of(a).pipe(endWith(b, c, d, e, f)); // $ExpectType Observable<A | B | C | D | E | F>
const r6 = of(a).pipe(endWith(b, c, d, e, f, g)); // $ExpectType Observable<A | B | C | D | E | F | G>
const r7 = of(a).pipe(endWith(b, c, d, e, f, g, h)); // $ExpectType Observable<A | B | C | D | E | F | G | H>
});
75 changes: 43 additions & 32 deletions src/internal/operators/endWith.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Observable } from '../Observable';
import { concat } from '../observable/concat';
import { of } from '../observable/of';
import { MonoTypeOperatorFunction, SchedulerLike, OperatorFunction } from '../types';
import { MonoTypeOperatorFunction, SchedulerLike, OperatorFunction, ValueFromArray } from '../types';

/* tslint:disable:max-line-length */
/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([source, [a, b, c]], scheduler).pipe(concatAll())`) */
Expand All @@ -19,49 +19,60 @@ export function endWith<T, A, B, C, D, E>(v1: A, v2: B, v3: C, v4: D, v5: E, sch
/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([source, [a, b, c]], scheduler).pipe(concatAll())`) */
export function endWith<T, A, B, C, D, E, F>(v1: A, v2: B, v3: C, v4: D, v5: E, v6: F, scheduler: SchedulerLike): OperatorFunction<T, T | A | B | C | D | E | F>;

export function endWith<T, A>(v1: A): OperatorFunction<T, T | A>;
export function endWith<T, A, B>(v1: A, v2: B): OperatorFunction<T, T | A | B>;
export function endWith<T, A, B, C>(v1: A, v2: B, v3: C): OperatorFunction<T, T | A | B | C>;
export function endWith<T, A, B, C, D>(v1: A, v2: B, v3: C, v4: D): OperatorFunction<T, T | A | B | C | D>;
export function endWith<T, A, B, C, D, E>(v1: A, v2: B, v3: C, v4: D, v5: E): OperatorFunction<T, T | A | B | C | D | E>;
export function endWith<T, A, B, C, D, E, F>(v1: A, v2: B, v3: C, v4: D, v5: E, v6: F): OperatorFunction<T, T | A | B | C | D | E | F>;
export function endWith<T, Z = T>(...array: Z[]): OperatorFunction<T, T | Z>;
/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([source, [a, b, c]], scheduler).pipe(concatAll())`) */
export function endWith<T, Z = T>(...array: Array<Z | SchedulerLike>): OperatorFunction<T, T | Z>;
export function endWith<T, A extends any[]>(...args: A): OperatorFunction<T, T | ValueFromArray<A>>;

/* tslint:enable:max-line-length */

/**
* Returns an Observable that emits the items you specify as arguments after it finishes emitting
* items emitted by the source Observable.
* Returns an observable that will emit all values from the source, then synchronously emit
* he provided value(s) immediately after the source completes.
*
* NOTE: Passing a last argument of a Scheduler is _deprecated_, and may result in incorrect
* types in TypeScript.
*
* This is useful for knowing when an observable ends. Particularly when paired with an
* operator like {@link takeUntil}
*
* ![](endWith.png)
*
* ## Example
* ### After the source observable completes, appends an emission and then completes too.
*
* Emit values to know when an interval starts and stops. The interval will
* stop when a user clicks anywhere on the document.
*
* ```ts
* import { of } from 'rxjs';
* import { endWith } from 'rxjs/operators';
* import { interval, fromEvent } from 'rxjs';
* import { map, startWith, takeUntil, endWith } from 'rxjs/operators';
*
* of('hi', 'how are you?', 'sorry, I have to go now').pipe(
* endWith('goodbye!'),
* const ticker$ = interval(5000).pipe(
* map(() => 'tick'),
* );
*
* const documentClicks$ = fromEvent(document, 'click');
*
* ticker$.pipe(
* startWith('interval started'),
* takeUntil(documentClicks$),
* endWith('interval ended by click'),
* )
* .subscribe(
* x = console.log(x);
* )
* .subscribe(word => console.log(word));
* // result:
* // 'hi'
* // 'how are you?'
* // 'sorry, I have to go now'
* // 'goodbye!'
*
* // Result (assuming a user clicks after 15 seconds)
* // "interval started"
* // "tick"
* // "tick"
* // "tick"
* // "interval ended by click"
* ```
*
* @param {...T} values - Items you want the modified Observable to emit last.
* @param {SchedulerLike} [scheduler] - A {@link SchedulerLike} to use for scheduling
* the emissions of the `next` notifications.
* @return {Observable} An Observable that emits the items emitted by the source Observable
* and then emits the items in the specified Iterable.
* @method endWith
* @owner Observable
* @param values - Items you want the modified Observable to emit last.
*
* @see startWith
* @see concat
* @see takeUntil
*/
export function endWith<T>(...array: Array<T | SchedulerLike>): MonoTypeOperatorFunction<T> {
return (source: Observable<T>) => concat(source, of(...array)) as Observable<T>;
export function endWith<T>(...values: Array<T | SchedulerLike>): MonoTypeOperatorFunction<T> {
return (source: Observable<T>) => concat(source, of(...values)) as Observable<T>;
}