Skip to content

Commit

Permalink
fix: support N args in static race and ensure observable returned (#5286
Browse files Browse the repository at this point in the history
)

* test: add failing test for race

* fix: use from on ObservableInput

* fix: support N args for static race

* chore: use dtslint helpers
  • Loading branch information
cartant authored Feb 7, 2020
1 parent ab6e9fc commit 6d901cb
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 129 deletions.
122 changes: 17 additions & 105 deletions spec-dtslint/observables/race-spec.ts
Original file line number Diff line number Diff line change
@@ -1,120 +1,32 @@
import { race, of } from 'rxjs';

it('should infer correctly with 1 parameter', () => {
const a = of(1);
const o = race(a); // $ExpectType Observable<number>
});
import { a$, b, b$, c, c$, d$, e$, f$ } from '../helpers';

describe('race(a, b, c)', () => {
it('should infer correctly with multiple parameters of the same type', () => {
const a = of(1);
const b = of(2);
const o = race(a, b); // $ExpectType Observable<number>
});

it('should support 2 parameters with different types', () => {
const a = of(1);
const b = of('a');
const o = race(a, b); // $ExpectType Observable<string | number>
});

it('should support 3 parameters with different types', () => {
const a = of(1);
const b = of('a');
const c = of(true);
const o = race(a, b, c); // $ExpectType Observable<string | number | boolean>
});

it('should support 4 parameters with different types', () => {
const a = of(1);
const b = of('a');
const c = of(true);
const d = of([1, 2, 3]);
const o = race(a, b, c, d); // $ExpectType Observable<string | number | boolean | number[]>
});

it('should support 5 parameters with different types', () => {
const a = of(1);
const b = of('a');
const c = of(true);
const d = of([1, 2, 3]);
const e = of(['blah']);
const o = race(a, b, c, d, e); // $ExpectType Observable<string | number | boolean | number[] | string[]>
});

it('should support 6 or more parameters of the same type', () => {
const a = of(1);
const o = race(a, a, a, a, a, a, a, a, a, a, a, a, a, a); // $ExpectType Observable<number>
});

it('should return unknown for 6 or more arguments of different types', () => {
const a = of(1);
const b = of('a');
const c = of(true);
const d = of([1, 2, 3]);
const e = of(['blah']);
const f = of({ foo: 'bar' });
const o = race(a, b, c, d, e, f); // $ExpectType Observable<unknown>
it('should support N arguments of different types', () => {
const o1 = race(a$); // $ExpectType Observable<A>
const o2 = race(a$, b$); // $ExpectType Observable<A | B>
const o3 = race(a$, b$, c$); // $ExpectType Observable<A | B | C>
const o4 = race(a$, b$, c$, d$); // $ExpectType Observable<A | B | C | D>
const o5 = race(a$, b$, c$, d$, e$); // $ExpectType Observable<A | B | C | D | E>
const o6 = race(a$, b$, c$, d$, e$, f$); // $ExpectType Observable<A | B | C | D | E | F>
});
});

describe('race([a, b, c])', () => {
it('should infer correctly with multiple parameters of the same type', () => {
const a = of(1);
const b = of(2);
const o = race([a, b]); // $ExpectType Observable<number>
});

it('should support 2 parameters with different types', () => {
const a = of(1);
const b = of('a');
const o = race([a, b]); // $ExpectType Observable<string | number>
});

it('should support 3 parameters with different types', () => {
const a = of(1);
const b = of('a');
const c = of(true);
const o = race([a, b, c]); // $ExpectType Observable<string | number | boolean>
});

it('should support 4 parameters with different types', () => {
const a = of(1);
const b = of('a');
const c = of(true);
const d = of([1, 2, 3]);
const o = race([a, b, c, d]); // $ExpectType Observable<string | number | boolean | number[]>
});

it('should support 5 parameters with different types', () => {
const a = of(1);
const b = of('a');
const c = of(true);
const d = of([1, 2, 3]);
const e = of(['blah']);
const o = race([a, b, c, d, e]); // $ExpectType Observable<string | number | boolean | number[] | string[]>
});

it('should support 6 or more parameters of the same type', () => {
const a = of(1);
const o = race([a, a, a, a, a, a, a, a, a, a, a, a, a, a]); // $ExpectType Observable<number>
});

it('should return {} for 6 or more arguments of different types', () => {
const a = of(1);
const b = of('a');
const c = of(true);
const d = of([1, 2, 3]);
const e = of(['blah']);
const f = of({ foo: 'bar' });
const o = race([a, b, c, d, e, f]); // $ExpectType Observable<unknown>
it('should support N arguments of different types', () => {
const o1 = race([a$]); // $ExpectType Observable<A>
const o2 = race([a$, b$]); // $ExpectType Observable<A | B>
const o3 = race([a$, b$, c$]); // $ExpectType Observable<A | B | C>
const o4 = race([a$, b$, c$, d$]); // $ExpectType Observable<A | B | C | D>
const o5 = race([a$, b$, c$, d$, e$]); // $ExpectType Observable<A | B | C | D | E>
const o6 = race([a$, b$, c$, d$, e$, f$]); // $ExpectType Observable<A | B | C | D | E | F>
});
});

it('should race observable inputs', () => {
const o = race(of(1), Promise.resolve('foo'), [true, false]); // $ExpectType Observable<string | number | boolean>
const o = race(a$, Promise.resolve(b), [c]); // $ExpectType Observable<A | B | C>
});

it('should race an array observable inputs', () => {
const o = race([of(1), Promise.resolve('foo'), [true, false]]); // $ExpectType Observable<string | number | boolean>
const o = race([a$, Promise.resolve(b), [c]]); // $ExpectType Observable<A | B | C>
});
8 changes: 8 additions & 0 deletions spec/observables/race-spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing';
import { race, of } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import { expect } from 'chai';

/** @test {race} */
describe('static race', () => {
Expand Down Expand Up @@ -204,4 +205,11 @@ describe('static race', () => {
expectObservable(source).toBe(expected);
expectSubscriptions(e1.subscriptions).toBe(e1subs);
});

it('should support a single ObservableInput argument', (done: MochaDone) => {
const source = race(Promise.resolve(42));
source.subscribe(value => {
expect(value).to.equal(42);
}, done, done);
});
});
31 changes: 7 additions & 24 deletions src/internal/observable/race.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,17 @@
import { Observable } from '../Observable';
import { isArray } from '../util/isArray';
import { from } from './from';
import { fromArray } from './fromArray';
import { Operator } from '../Operator';
import { Subscriber } from '../Subscriber';
import { Subscription } from '../Subscription';
import { TeardownLogic, ObservableInput } from '../types';
import { TeardownLogic, ObservableInput, ObservedValueUnionFromArray } from '../types';
import { OuterSubscriber } from '../OuterSubscriber';
import { InnerSubscriber } from '../InnerSubscriber';
import { subscribeToResult } from '../util/subscribeToResult';

// tslint:disable:max-line-length
export function race<A>(arg: [ObservableInput<A>]): Observable<A>;
export function race<A, B>(arg: [ObservableInput<A>, ObservableInput<B>]): Observable<A | B>;
export function race<A, B, C>(arg: [ObservableInput<A>, ObservableInput<B>, ObservableInput<C>]): Observable<A | B | C>;
export function race<A, B, C, D>(arg: [ObservableInput<A>, ObservableInput<B>, ObservableInput<C>, ObservableInput<D>]): Observable<A | B | C | D>;
export function race<A, B, C, D, E>(arg: [ObservableInput<A>, ObservableInput<B>, ObservableInput<C>, ObservableInput<D>, ObservableInput<E>]): Observable<A | B | C | D | E>;
export function race<T>(arg: ObservableInput<T>[]): Observable<T>;
export function race(arg: ObservableInput<any>[]): Observable<unknown>;

export function race<A>(a: ObservableInput<A>): Observable<A>;
export function race<A, B>(a: ObservableInput<A>, b: ObservableInput<B>): Observable<A | B>;
export function race<A, B, C>(a: ObservableInput<A>, b: ObservableInput<B>, c: ObservableInput<C>): Observable<A | B | C>;
export function race<A, B, C, D>(a: ObservableInput<A>, b: ObservableInput<B>, c: ObservableInput<C>, d: ObservableInput<D>): Observable<A | B | C | D>;
export function race<A, B, C, D, E>(a: ObservableInput<A>, b: ObservableInput<B>, c: ObservableInput<C>, d: ObservableInput<D>, e: ObservableInput<E>): Observable<A | B | C | D | E>;
// tslint:enable:max-line-length

export function race<T>(observables: ObservableInput<T>[]): Observable<T>;
export function race(observables: ObservableInput<any>[]): Observable<unknown>;
export function race<T>(...observables: ObservableInput<T>[]): Observable<T>;
export function race(...observables: ObservableInput<any>[]): Observable<unknown>;
export function race<A extends ObservableInput<any>[]>(observables: A): Observable<ObservedValueUnionFromArray<A>>;
export function race<A extends ObservableInput<any>[]>(...observables: A): Observable<ObservedValueUnionFromArray<A>>;

/**
* Returns an observable that mirrors the first source observable to emit an item.
Expand Down Expand Up @@ -71,14 +54,14 @@ export function race(...observables: ObservableInput<any>[]): Observable<unknown
* @param {...Observables} ...observables sources used to race for which Observable emits first.
* @return {Observable} an Observable that mirrors the output of the first Observable to emit an item.
*/
export function race<T>(...observables: ObservableInput<any>[]): Observable<T> {
export function race<T>(...observables: (ObservableInput<T> | ObservableInput<T>[])[]): Observable<any> {
// if the only argument is an array, it was most likely called with
// `race([obs1, obs2, ...])`
if (observables.length === 1) {
if (isArray(observables[0])) {
observables = observables[0] as Observable<any>[];
observables = observables[0] as ObservableInput<T>[];
} else {
return observables[0] as Observable<T>;
return from(observables[0] as ObservableInput<T>);
}
}

Expand Down

0 comments on commit 6d901cb

Please sign in to comment.