Skip to content

Commit

Permalink
fix(race): better typings (#4643)
Browse files Browse the repository at this point in the history
- Actually accept other `ObservableInput` types (Promises, etc)
- Fixes issue where TypeScript would complain when you tried to subscribe to a race between to Observable types.

Fixes #4390
Fixes #4642
  • Loading branch information
benlesh authored Mar 15, 2019
1 parent 8c5d831 commit fb9bc48
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 60 deletions.
151 changes: 101 additions & 50 deletions spec-dtslint/observables/race-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,65 +5,116 @@ it('should infer correctly with 1 parameter', () => {
const o = race(a); // $ExpectType Observable<number>
});

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>
});
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> | 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> | Observable<number> | Observable<boolean>
});
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> | Observable<number> | Observable<boolean> | Observable<number[]>
});
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> | Observable<number> | Observable<boolean> | Observable<number[]> | Observable<string[]>
});
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 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<{}>
});
});

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<{}>
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<{}>
});
});

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

it('should return {} for array of observables of different types', () => {
const a = of(1);
const b = of('test');
const o = race([a, b]); // $ExpectType Observable<{}>
it('should race an array observable inputs', () => {
const o = race([of(1), Promise.resolve('foo'), [true, false]]); // $ExpectType Observable<string | number | boolean>
});
29 changes: 19 additions & 10 deletions src/internal/observable/race.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,31 @@ import { fromArray } from './fromArray';
import { Operator } from '../Operator';
import { Subscriber } from '../Subscriber';
import { Subscription } from '../Subscription';
import { TeardownLogic } from '../types';
import { TeardownLogic, ObservableInput } from '../types';
import { OuterSubscriber } from '../OuterSubscriber';
import { InnerSubscriber } from '../InnerSubscriber';
import { subscribeToResult } from '../util/subscribeToResult';

// tslint:disable:max-line-length
export function race<A, B>(a: Observable<A>, b: Observable<B>): Observable<A> | Observable<B>;
export function race<A, B, C>(a: Observable<A>, b: Observable<B>, c: Observable<C>): Observable<A> | Observable<B> | Observable<C>;
export function race<A, B, C, D>(a: Observable<A>, b: Observable<B>, c: Observable<C>, d: Observable<D>): Observable<A> | Observable<B> | Observable<C> | Observable<D>;
export function race<A, B, C, D, E>(a: Observable<A>, b: Observable<B>, c: Observable<C>, d: Observable<D>, e: Observable<E>): Observable<A> | Observable<B> | Observable<C> | Observable<D> | Observable<E>;
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<{}>;

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: Observable<T>[]): Observable<T>;
export function race(observables: Observable<any>[]): Observable<{}>;
export function race<T>(...observables: Observable<T>[]): Observable<T>;
export function race(...observables: Observable<any>[]): Observable<{}>;
export function race<T>(observables: ObservableInput<T>[]): Observable<T>;
export function race(observables: ObservableInput<any>[]): Observable<{}>;
export function race<T>(...observables: ObservableInput<T>[]): Observable<T>;
export function race(...observables: ObservableInput<any>[]): Observable<{}>;

/**
* Returns an Observable that mirrors the first source Observable to emit an item.
Expand Down Expand Up @@ -50,7 +59,7 @@ export function race(...observables: Observable<any>[]): Observable<{}>;
* @name race
* @owner Observable
*/
export function race<T>(...observables: (Observable<any>[] | Observable<any>)[]): Observable<T> {
export function race<T>(...observables: ObservableInput<any>[]): Observable<T> {
// if the only argument is an array, it was most likely called with
// `race([obs1, obs2, ...])`
if (observables.length === 1) {
Expand Down

0 comments on commit fb9bc48

Please sign in to comment.