Skip to content

Commit 126d2b6

Browse files
david-driscollbenlesh
authored andcommittedMay 10, 2019
fix(scan): fixed declarations to properly support different return types (#4598)
this solves an issue where if you would reduce over `T` you can get `T` out reliably, but you could not get out `R` in anyway without manually typing the reduce method call
1 parent da69c16 commit 126d2b6

File tree

8 files changed

+67
-24
lines changed

8 files changed

+67
-24
lines changed
 

‎compat/operator/reduce.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { Observable } from 'rxjs';
22
import { reduce as higherOrderReduce } from 'rxjs/operators';
33

44
/* tslint:disable:max-line-length */
5-
export function reduce<T>(this: Observable<T>, accumulator: (acc: T, value: T, index: number) => T, seed?: T): Observable<T>;
6-
export function reduce<T>(this: Observable<T>, accumulator: (acc: T[], value: T, index: number) => T[], seed: T[]): Observable<T[]>;
75
export function reduce<T, R>(this: Observable<T>, accumulator: (acc: R, value: T, index: number) => R, seed: R): Observable<R>;
6+
export function reduce<T>(this: Observable<T>, accumulator: (acc: T, value: T, index: number) => T, seed?: T): Observable<T>;
7+
export function reduce<T, R>(this: Observable<T>, accumulator: (acc: R, value: T, index: number) => R): Observable<R>;
88
/* tslint:enable:max-line-length */
99

1010
/**
@@ -51,7 +51,7 @@ export function reduce<T, R>(this: Observable<T>, accumulator: (acc: R, value: T
5151
* @method reduce
5252
* @owner Observable
5353
*/
54-
export function reduce<T, R>(this: Observable<T>, accumulator: (acc: R, value: T, index?: number) => R, seed?: R): Observable<R> {
54+
export function reduce<T, R>(this: Observable<T>, accumulator: (acc: T | R, value: T, index?: number) => (T | R), seed?: R): Observable<T | R> {
5555
// providing a seed of `undefined` *should* be valid and trigger
5656
// hasSeed! so don't use `seed !== undefined` checks!
5757
// For this reason, we have to check it here at the original call site
@@ -61,5 +61,5 @@ export function reduce<T, R>(this: Observable<T>, accumulator: (acc: R, value: T
6161
return higherOrderReduce(accumulator, seed)(this);
6262
}
6363

64-
return higherOrderReduce(accumulator)(this);
64+
return higherOrderReduce<T, T | R>(accumulator)(this);
6565
}

‎compat/operator/scan.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { Observable } from 'rxjs';
33
import { scan as higherOrderScan } from 'rxjs/operators';
44

55
/* tslint:disable:max-line-length */
6+
export function scan<T, R>(this: Observable<T>, accumulator: (acc: R, value: T, index: number) => R, seed: R): Observable<R>;
67
export function scan<T>(this: Observable<T>, accumulator: (acc: T, value: T, index: number) => T, seed?: T): Observable<T>;
7-
export function scan<T>(this: Observable<T>, accumulator: (acc: T[], value: T, index: number) => T[], seed?: T[]): Observable<T[]>;
8-
export function scan<T, R>(this: Observable<T>, accumulator: (acc: R, value: T, index: number) => R, seed?: R): Observable<R>;
8+
export function scan<T, R>(this: Observable<T>, accumulator: (acc: R, value: T, index: number) => R): Observable<R>;
99
/* tslint:enable:max-line-length */
1010

1111
/**
@@ -46,10 +46,10 @@ export function scan<T, R>(this: Observable<T>, accumulator: (acc: R, value: T,
4646
* @owner Observable
4747
*/
4848
export function scan<T, R>(this: Observable<T>,
49-
accumulator: (acc: T | Array<T> | R, value: T, index: number) => T | Array<T> | R,
50-
seed?: T | R): Observable<R> {
49+
accumulator: (acc: T | R, value: T, index: number) => T | R,
50+
seed?: T | R): Observable<T | R> {
5151
if (arguments.length >= 2) {
52-
return higherOrderScan(accumulator, seed)(this) as Observable<R>;
52+
return higherOrderScan(accumulator, seed)(this);
5353
}
54-
return higherOrderScan(accumulator)(this) as Observable<R>;
54+
return higherOrderScan<T, T | R>(accumulator)(this);
5555
}

‎spec-dtslint/operators/reduce-spec.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,17 @@ it('should infer correctly for accumulator of type array', () => {
1515

1616
it('should accept seed parameter of the same type', () => {
1717
const a = of(1, 2, 3).pipe(reduce((x, y, z) => x + 1, 5)); // $ExpectType Observable<number>
18-
const b = of(1, 2, 3).pipe(reduce((x, y, z) => x + 1, '5')); // $ExpectError
18+
const b = of(1, 2, 3).pipe(reduce((x, y, z) => x + 1, [])); // $ExpectError
19+
});
20+
21+
it('should accept seed parameter of the seed array type', () => {
22+
const a = of(1, 2, 3).pipe(reduce((x, y, z) => { x.push(y); return x; }, [4])); // $ExpectType Observable<number[]>
23+
// Array must be typed...
24+
const b = of(1, 2, 3).pipe(reduce((x, y, z) => { x.push(y); return x; }, [])); // $ExpectError
25+
});
26+
27+
it('should accept seed parameter of a different type', () => {
28+
const a = of(1, 2, 3).pipe(reduce((x, y, z) => x + '1', '5')); // $ExpectType Observable<string>
29+
const bv: { [key: string]: string } = {};
30+
const b = of(1, 2, 3).pipe(reduce((x, y, z) => ({ ...x, [y]: y.toString() }), bv)); // $ExpectType Observable<{ [key: string]: string; }>
1931
});

‎spec-dtslint/operators/scan-spec.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { of, Observable } from 'rxjs';
2+
import { scan } from 'rxjs/operators';
3+
4+
it('should enforce parameter', () => {
5+
const a = of(1, 2, 3).pipe(scan()); // $ExpectError
6+
});
7+
8+
it('should infer correctly ', () => {
9+
const a = of(1, 2, 3).pipe(scan((x, y, z) => x + 1)); // $ExpectType Observable<number>
10+
});
11+
12+
it('should infer correctly for accumulator of type array', () => {
13+
const a = of(1, 2, 3).pipe(scan((x: number[], y: number, i: number) => x, [])); // $ExpectType Observable<number[]>
14+
});
15+
16+
it('should accept seed parameter of the same type', () => {
17+
const a = of(1, 2, 3).pipe(scan((x, y, z) => x + 1, 5)); // $ExpectType Observable<number>
18+
const b = of(1, 2, 3).pipe(scan((x, y, z) => x + 1, [])); // $ExpectError
19+
});
20+
21+
it('should accept seed parameter of the seed array type', () => {
22+
const a = of(1, 2, 3).pipe(scan((x, y, z) => { x.push(y); return x; }, [4])); // $ExpectType Observable<number[]>
23+
// Array must be typed...
24+
const b = of(1, 2, 3).pipe(scan((x, y, z) => { x.push(y); return x; }, [])); // $ExpectError
25+
});
26+
27+
it('should accept seed parameter of a different type', () => {
28+
const a = of(1, 2, 3).pipe(scan((x, y, z) => x + '1', '5')); // $ExpectType Observable<string>
29+
const bv: { [key: string]: string } = {};
30+
const b = of(1, 2, 3).pipe(scan((x, y, z) => ({ ...x, [y]: y.toString() }), bv)); // $ExpectType Observable<{ [key: string]: string; }>
31+
});

‎spec/operators/reduce-spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ describe('reduce operator', () => {
292292

293293
type('should accept array typed reducers', () => {
294294
let a: Observable<{ a: number; b: string }>;
295-
a.pipe(reduce<{ a: number; b: string }>((acc, value) => acc.concat(value), []));
295+
a.pipe(reduce((acc, value) => acc.concat(value), []));
296296
});
297297

298298
type('should accept T typed reducers', () => {
@@ -322,7 +322,7 @@ describe('reduce operator', () => {
322322

323323
type('should accept R typed reduces when R is an array of T', () => {
324324
let a: Observable<number>;
325-
const reduced = a.pipe(reduce<number>((acc, value) => {
325+
const reduced = a.pipe(reduce((acc, value) => {
326326
acc.push(value);
327327
return acc;
328328
}, []));

‎spec/operators/scan-spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -230,12 +230,12 @@ describe('scan operator', () => {
230230

231231
type('should accept array typed reducers', () => {
232232
let a: Observable<{ a: number; b: string }>;
233-
a.pipe(reduce<{ a: number; b: string }>((acc, value) => acc.concat(value), []));
233+
a.pipe(scan((acc, value) => acc.concat(value), []));
234234
});
235235

236236
type('should accept T typed reducers', () => {
237237
let a: Observable<{ a?: number; b?: string }>;
238-
a.pipe(reduce((acc, value) => {
238+
a.pipe(scan((acc, value) => {
239239
value.a = acc.a;
240240
value.b = acc.b;
241241
return acc;
@@ -244,7 +244,7 @@ describe('scan operator', () => {
244244

245245
type('should accept R typed reducers', () => {
246246
let a: Observable<{ a: number; b: string }>;
247-
a.pipe(reduce<{ a?: number; b?: string }>((acc, value) => {
247+
a.pipe(scan<{ a?: number; b?: string }>((acc, value) => {
248248
value.a = acc.a;
249249
value.b = acc.b;
250250
return acc;

‎src/internal/operators/reduce.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import { OperatorFunction, MonoTypeOperatorFunction } from '../types';
66
import { pipe } from '../util/pipe';
77

88
/* tslint:disable:max-line-length */
9+
export function reduce<T, R>(accumulator: (acc: R, value: T, index: number) => R, seed: R): OperatorFunction<T, R>;
910
export function reduce<T>(accumulator: (acc: T, value: T, index: number) => T, seed?: T): MonoTypeOperatorFunction<T>;
10-
export function reduce<T>(accumulator: (acc: T[], value: T, index: number) => T[], seed: T[]): OperatorFunction<T, T[]>;
11-
export function reduce<T, R>(accumulator: (acc: R, value: T, index: number) => R, seed?: R): OperatorFunction<T, R>;
11+
export function reduce<T, R>(accumulator: (acc: R, value: T, index: number) => R): OperatorFunction<T, R>;
1212
/* tslint:enable:max-line-length */
1313

1414
/**
@@ -62,20 +62,20 @@ export function reduce<T, R>(accumulator: (acc: R, value: T, index: number) => R
6262
* @method reduce
6363
* @owner Observable
6464
*/
65-
export function reduce<T, R>(accumulator: (acc: R, value: T, index?: number) => R, seed?: R): OperatorFunction<T, R> {
65+
export function reduce<T, R>(accumulator: (acc: T | R, value: T, index?: number) => T | R, seed?: T | R): OperatorFunction<T, T | R> {
6666
// providing a seed of `undefined` *should* be valid and trigger
6767
// hasSeed! so don't use `seed !== undefined` checks!
6868
// For this reason, we have to check it here at the original call site
6969
// otherwise inside Operator/Subscriber we won't know if `undefined`
7070
// means they didn't provide anything or if they literally provided `undefined`
7171
if (arguments.length >= 2) {
72-
return function reduceOperatorFunctionWithSeed(source: Observable<T>): Observable<R> {
72+
return function reduceOperatorFunctionWithSeed(source: Observable<T>): Observable<T | R> {
7373
return pipe(scan(accumulator, seed), takeLast(1), defaultIfEmpty(seed))(source);
7474
};
7575
}
76-
return function reduceOperatorFunction(source: Observable<T>): Observable<R> {
76+
return function reduceOperatorFunction(source: Observable<T>): Observable<T | R> {
7777
return pipe(
78-
scan((acc: R, value: T, index: number): R => accumulator(acc, value, index + 1)),
78+
scan<T, T | R>((acc, value, index) => accumulator(acc, value, index + 1)),
7979
takeLast(1),
8080
)(source);
8181
};

‎src/internal/operators/scan.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { Subscriber } from '../Subscriber';
44
import { OperatorFunction, MonoTypeOperatorFunction } from '../types';
55

66
/* tslint:disable:max-line-length */
7+
export function scan<T, R>(accumulator: (acc: R, value: T, index: number) => R, seed: R): OperatorFunction<T, R>;
78
export function scan<T>(accumulator: (acc: T, value: T, index: number) => T, seed?: T): MonoTypeOperatorFunction<T>;
8-
export function scan<T>(accumulator: (acc: T[], value: T, index: number) => T[], seed?: T[]): OperatorFunction<T, T[]>;
9-
export function scan<T, R>(accumulator: (acc: R, value: T, index: number) => R, seed?: R): OperatorFunction<T, R>;
9+
export function scan<T, R>(accumulator: (acc: R, value: T, index: number) => R): OperatorFunction<T, R>;
1010
/* tslint:enable:max-line-length */
1111

1212
/**

0 commit comments

Comments
 (0)
Please sign in to comment.