Skip to content

Commit c2ac39c

Browse files
committed
fix(types): support union type inference for merge operators
1 parent ebfb11a commit c2ac39c

File tree

12 files changed

+75
-51
lines changed

12 files changed

+75
-51
lines changed

spec-dtslint/observables/concat-spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,14 @@ it('should enforce types', () => {
5252
const o = concat(5); // $ExpectError
5353
const p = concat(of(5), 6); // $ExpectError
5454
});
55+
56+
it('should support union types', () => {
57+
const u = Math.random() > 0.5 ? of(123) : of('abc');
58+
const o = concat(u, u, u); // $ExpectType Observable<string | number>
59+
});
60+
61+
it('should support different union types', () => {
62+
const u1 = Math.random() > 0.5 ? of(123) : of('abc');
63+
const u2 = Math.random() > 0.5 ? of(true) : of([1, 2, 3]);
64+
const o = concat(u1, u2); // $ExpectType Observable<string | number | boolean | number[]>
65+
});

spec-dtslint/operators/concatMap-spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ it('should support an undefined resultSelector', () => {
2929
const o = of(1, 2, 3).pipe(concatMap(p => of(Boolean(p)), undefined)); // $ExpectType Observable<boolean>
3030
});
3131

32+
it('should support union-type projections', () => {
33+
const o = of(Math.random()).pipe(concatMap(n => n > 0.5 ? of('life') : of(42))); // $ExpectType Observable<string | number>
34+
});
35+
3236
it('should enforce types', () => {
3337
const o = of(1, 2, 3).pipe(concatMap()); // $ExpectError
3438
});

spec-dtslint/operators/concatMapTo-spec.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ it('should support an undefined resultSelector', () => {
3737
const o = of(1, 2, 3).pipe(concatMapTo(of('foo'), undefined)); // $ExpectType Observable<string>
3838
});
3939

40+
it('should support union types', () => {
41+
const s = Math.random() > 0.5 ? of(123) : of('abc');
42+
const r = of(1, 2, 3).pipe(concatMapTo(s)); // $ExpectType<string | number>
43+
});
44+
4045
it('should enforce types', () => {
4146
const o = of(1, 2, 3).pipe(concatMapTo()); // $ExpectError
4247
});

spec-dtslint/operators/mergeMap-spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ it('should support a undefined resultSelector and concurrent parameter', () => {
4141
const o = of(1, 2, 3).pipe(mergeMap(p => of(Boolean(p)), undefined, 4)); // $ExpectType Observable<boolean>
4242
});
4343

44+
it('should support union-type projections', () => {
45+
const o = of(Math.random()).pipe(mergeMap(n => n > 0.5 ? of('life') : of(42))); // $ExpectType Observable<string | number>
46+
});
47+
4448
it('should enforce types', () => {
4549
const o = of(1, 2, 3).pipe(mergeMap()); // $ExpectError
4650
});

spec-dtslint/operators/mergeMapTo-spec.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ it('should support a resultSelector and concurrent parameter', () => {
4141
const o = of(1, 2, 3).pipe(mergeMapTo(of('foo'), (a, b) => b, 4)); // $ExpectType Observable<string>
4242
});
4343

44+
it('should support union types', () => {
45+
const s = Math.random() > 0.5 ? of(123) : of('abc');
46+
const r = of(1, 2, 3).pipe(mergeMapTo(s)); // $ExpectType<string | number>
47+
});
48+
4449
it('should enforce types', () => {
4550
const o = of(1, 2, 3).pipe(mergeMapTo()); // $ExpectError
4651
});

src/internal/observable/concat.ts

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
import { Observable } from '../Observable';
2-
import { ObservableInput, SchedulerLike } from '../types';
2+
import { ObservableInput, SchedulerLike, ObservedValueOf } from '../types';
33
import { isScheduler } from '../util/isScheduler';
44
import { of } from './of';
55
import { from } from './from';
66
import { concatAll } from '../operators/concatAll';
77

88
/* tslint:disable:max-line-length */
9-
export function concat<T>(v1: ObservableInput<T>, scheduler?: SchedulerLike): Observable<T>;
10-
export function concat<T, T2>(v1: ObservableInput<T>, v2: ObservableInput<T2>, scheduler?: SchedulerLike): Observable<T | T2>;
11-
export function concat<T, T2, T3>(v1: ObservableInput<T>, v2: ObservableInput<T2>, v3: ObservableInput<T3>, scheduler?: SchedulerLike): Observable<T | T2 | T3>;
12-
export function concat<T, T2, T3, T4>(v1: ObservableInput<T>, v2: ObservableInput<T2>, v3: ObservableInput<T3>, v4: ObservableInput<T4>, scheduler?: SchedulerLike): Observable<T | T2 | T3 | T4>;
13-
export function concat<T, T2, T3, T4, T5>(v1: ObservableInput<T>, v2: ObservableInput<T2>, v3: ObservableInput<T3>, v4: ObservableInput<T4>, v5: ObservableInput<T5>, scheduler?: SchedulerLike): Observable<T | T2 | T3 | T4 | T5>;
14-
export function concat<T, T2, T3, T4, T5, T6>(v1: ObservableInput<T>, v2: ObservableInput<T2>, v3: ObservableInput<T3>, v4: ObservableInput<T4>, v5: ObservableInput<T5>, v6: ObservableInput<T6>, scheduler?: SchedulerLike): Observable<T | T2 | T3 | T4 | T5 | T6>;
15-
export function concat<T>(...observables: (ObservableInput<T> | SchedulerLike)[]): Observable<T>;
16-
export function concat<T, R>(...observables: (ObservableInput<any> | SchedulerLike)[]): Observable<R>;
9+
export function concat<O1 extends ObservableInput<any>>(v1: O1, scheduler?: SchedulerLike): Observable<ObservedValueOf<O1>>;
10+
export function concat<O1 extends ObservableInput<any>, O2 extends ObservableInput<any>>(v1: O1, v2: O2, scheduler?: SchedulerLike): Observable<ObservedValueOf<O1> | ObservedValueOf<O2>>;
11+
export function concat<O1 extends ObservableInput<any>, O2 extends ObservableInput<any>, O3 extends ObservableInput<any>>(v1: O1, v2: O2, v3: O3, scheduler?: SchedulerLike): Observable<ObservedValueOf<O1> | ObservedValueOf<O2> | ObservedValueOf<O3>>;
12+
export function concat<O1 extends ObservableInput<any>, O2 extends ObservableInput<any>, O3 extends ObservableInput<any>, O4 extends ObservableInput<any>>(v1: O1, v2: O2, v3: O3, v4: O4, scheduler?: SchedulerLike): Observable<ObservedValueOf<O1> | ObservedValueOf<O2> | ObservedValueOf<O3> | ObservedValueOf<O4>>;
13+
export function concat<O1 extends ObservableInput<any>, O2 extends ObservableInput<any>, O3 extends ObservableInput<any>, O4 extends ObservableInput<any>, O5 extends ObservableInput<any>>(v1: O1, v2: O2, v3: O3, v4: O4, v5: O5, scheduler?: SchedulerLike): Observable<ObservedValueOf<O1> | ObservedValueOf<O2> | ObservedValueOf<O3> | ObservedValueOf<O4> | ObservedValueOf<O5>>;
14+
export function concat<O1 extends ObservableInput<any>, O2 extends ObservableInput<any>, O3 extends ObservableInput<any>, O4 extends ObservableInput<any>, O5 extends ObservableInput<any>, O6 extends ObservableInput<any>>(v1: O1, v2: O2, v3: O3, v4: O4, v5: O5, v6: O6, scheduler?: SchedulerLike): Observable<ObservedValueOf<O1> | ObservedValueOf<O2> | ObservedValueOf<O3> | ObservedValueOf<O4> | ObservedValueOf<O5> | ObservedValueOf<O6>>;
15+
export function concat<O extends ObservableInput<any>>(...observables: (O | SchedulerLike)[]): Observable<ObservedValueOf<O>>;
16+
export function concat<O1 extends ObservableInput<any>, R>(...observables: (ObservableInput<any> | SchedulerLike)[]): Observable<R>;
1717
/* tslint:enable:max-line-length */
1818
/**
1919
* Creates an output Observable which sequentially emits all values from given
@@ -113,9 +113,6 @@ export function concat<T, R>(...observables: (ObservableInput<any> | SchedulerLi
113113
* @name concat
114114
* @owner Observable
115115
*/
116-
export function concat<T, R>(...observables: Array<ObservableInput<any> | SchedulerLike>): Observable<R> {
117-
if (observables.length === 1 || (observables.length === 2 && isScheduler(observables[1]))) {
118-
return from(<any>observables[0]);
119-
}
116+
export function concat<O extends ObservableInput<any>, R>(...observables: Array<O | SchedulerLike>): Observable<ObservedValueOf<O> | R> {
120117
return concatAll<R>()(of(...observables));
121118
}

src/internal/operators/concat.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ export function concat<T, R>(...observables: Array<ObservableInput<any> | Schedu
2525
* @deprecated Deprecated in favor of static {@link concat}.
2626
*/
2727
export function concat<T, R>(...observables: Array<ObservableInput<any> | SchedulerLike>): OperatorFunction<T, R> {
28-
return (source: Observable<T>) => source.lift.call(concatStatic<T, R>(source, ...observables));
28+
return (source: Observable<T>) => source.lift.call(concatStatic(source, ...observables));
2929
}

src/internal/operators/concatMap.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { mergeMap } from './mergeMap';
2-
import { ObservableInput, OperatorFunction } from '../types';
2+
import { ObservableInput, OperatorFunction, ObservedValueOf } from '../types';
33

44
/* tslint:disable:max-line-length */
5-
export function concatMap<T, R>(project: (value: T, index: number) => ObservableInput<R>): OperatorFunction<T, R>;
5+
export function concatMap<T, O extends ObservableInput<any>>(project: (value: T, index: number) => O): OperatorFunction<T, ObservedValueOf<O>>;
66
/** @deprecated resultSelector no longer supported, use inner map instead */
7-
export function concatMap<T, R>(project: (value: T, index: number) => ObservableInput<R>, resultSelector: undefined): OperatorFunction<T, R>;
7+
export function concatMap<T, O extends ObservableInput<any>>(project: (value: T, index: number) => O, resultSelector: undefined): OperatorFunction<T, ObservedValueOf<O>>;
88
/** @deprecated resultSelector no longer supported, use inner map instead */
9-
export function concatMap<T, I, R>(project: (value: T, index: number) => ObservableInput<I>, resultSelector: (outerValue: T, innerValue: I, outerIndex: number, innerIndex: number) => R): OperatorFunction<T, R>;
9+
export function concatMap<T, R, O extends ObservableInput<any>>(project: (value: T, index: number) => O, resultSelector: (outerValue: T, innerValue: ObservedValueOf<O>, outerIndex: number, innerIndex: number) => R): OperatorFunction<T, R>;
1010
/* tslint:enable:max-line-length */
1111

1212
/**
@@ -66,9 +66,9 @@ export function concatMap<T, I, R>(project: (value: T, index: number) => Observ
6666
* @method concatMap
6767
* @owner Observable
6868
*/
69-
export function concatMap<T, I, R>(
70-
project: (value: T, index: number) => ObservableInput<I>,
71-
resultSelector?: (outerValue: T, innerValue: I, outerIndex: number, innerIndex: number) => R
72-
): OperatorFunction<T, I|R> {
69+
export function concatMap<T, R, O extends ObservableInput<any>>(
70+
project: (value: T, index: number) => O,
71+
resultSelector?: (outerValue: T, innerValue: ObservedValueOf<O>, outerIndex: number, innerIndex: number) => R
72+
): OperatorFunction<T, ObservedValueOf<O>|R> {
7373
return mergeMap(project, resultSelector, 1);
7474
}

src/internal/operators/concatMapTo.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { concatMap } from './concatMap';
2-
import { ObservableInput, OperatorFunction } from '../types';
2+
import { ObservableInput, OperatorFunction, ObservedValueOf } from '../types';
33

44
/* tslint:disable:max-line-length */
5-
export function concatMapTo<T>(observable: ObservableInput<T>): OperatorFunction<any, T>;
5+
export function concatMapTo<T, O extends ObservableInput<any>>(observable: O): OperatorFunction<T, ObservedValueOf<O>>;
66
/** @deprecated */
7-
export function concatMapTo<T>(observable: ObservableInput<T>, resultSelector: undefined): OperatorFunction<any, T>;
7+
export function concatMapTo<T, O extends ObservableInput<any>>(observable: O, resultSelector: undefined): OperatorFunction<T, ObservedValueOf<O>>;
88
/** @deprecated */
9-
export function concatMapTo<T, I, R>(observable: ObservableInput<I>, resultSelector: (outerValue: T, innerValue: I, outerIndex: number, innerIndex: number) => R): OperatorFunction<T, R>;
9+
export function concatMapTo<T, R, O extends ObservableInput<any>>(observable: O, resultSelector: (outerValue: T, innerValue: ObservedValueOf<O>, outerIndex: number, innerIndex: number) => R): OperatorFunction<T, R>;
1010
/* tslint:enable:max-line-length */
1111

1212
/**
@@ -62,9 +62,9 @@ export function concatMapTo<T, I, R>(observable: ObservableInput<I>, resultSelec
6262
* @method concatMapTo
6363
* @owner Observable
6464
*/
65-
export function concatMapTo<T, I, R>(
66-
innerObservable: ObservableInput<I>,
67-
resultSelector?: (outerValue: T, innerValue: I, outerIndex: number, innerIndex: number) => R
68-
): OperatorFunction<T, R> {
65+
export function concatMapTo<T, R, O extends ObservableInput<any>>(
66+
innerObservable: O,
67+
resultSelector?: (outerValue: T, innerValue: ObservedValueOf<O>, outerIndex: number, innerIndex: number) => R
68+
): OperatorFunction<T, ObservedValueOf<O>|R> {
6969
return concatMap(() => innerObservable, resultSelector);
7070
}

src/internal/operators/mergeAll.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11

22
import { mergeMap } from './mergeMap';
33
import { identity } from '../util/identity';
4-
import { MonoTypeOperatorFunction, OperatorFunction, ObservableInput } from '../types';
5-
6-
export function mergeAll<T>(concurrent?: number): OperatorFunction<ObservableInput<T>, T>;
4+
import { OperatorFunction, ObservableInput } from '../types';
75

86
/**
97
* Converts a higher-order Observable into a first-order Observable which
@@ -57,6 +55,6 @@ export function mergeAll<T>(concurrent?: number): OperatorFunction<ObservableInp
5755
* @method mergeAll
5856
* @owner Observable
5957
*/
60-
export function mergeAll<T>(concurrent: number = Number.POSITIVE_INFINITY): MonoTypeOperatorFunction<T> {
61-
return mergeMap<T, T>(identity as (value: T, index: number) => ObservableInput<T>, concurrent);
58+
export function mergeAll<T>(concurrent: number = Number.POSITIVE_INFINITY): OperatorFunction<ObservableInput<T>, T> {
59+
return mergeMap(identity, concurrent);
6260
}

0 commit comments

Comments
 (0)