Skip to content

Commit

Permalink
fix(pipe): replace rest parameters overload (#3945)
Browse files Browse the repository at this point in the history
* chore(test): add dtslint files and script

* chore(dtslint): add zip operator

* chore(dtslint): add zip observable

* chore(dtslint): rename directory

* fix(pipe): replace rest parameters overload

Replace the rest parameters overload with a signature that also includes
the A-I type parameters.

Closes #3841

* test(first): fix problem exposed by pipe fix

* test(reduce): fix problem exposed by pipe fix

* test(scan): fix problem exposed by pipe fix

* test(startWith): fix problem exposed by pipe fix

* test(zipAll): fix problem exposed by pipe fix

* chore(dtslint): move files

* chore(dtslint): add generic custom operator test

* chore(pipe): remove redundant type parameter

* chore(pipe): remove rest params type param

* chore(dtslint): add pipe rest params test
  • Loading branch information
cartant authored and benlesh committed Jul 26, 2018
1 parent 9c12ec0 commit 872b0ec
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 15 deletions.
103 changes: 103 additions & 0 deletions spec-dtslint/Observable-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Observable, of, OperatorFunction } from 'rxjs';
import { mapTo } from 'rxjs/operators';

function a<I extends string, O extends string>(input: I, output: O): OperatorFunction<I, O>;
function a<I, O extends string>(output: O): OperatorFunction<I, O>;
function a<I, O extends string>(inputOrOutput: I | O, output?: O): OperatorFunction<I, O> {
return mapTo<I, O>(output === undefined ? inputOrOutput as O : output);
}

describe('pipe', () => {
it('should infer for no arguments', () => {
const o = of('foo').pipe(); // $ExpectType Observable<string>
});

it('should infer for 1 argument', () => {
const o = of('foo').pipe(a('1')); // $ExpectType Observable<"1">
});

it('should infer for 2 arguments', () => {
const o = of('foo').pipe(a('1'), a('2')); // $ExpectType Observable<"2">
});

it('should infer for 3 arguments', () => {
const o = of('foo').pipe(a('1'), a('2'), a('3')); // $ExpectType Observable<"3">
});

it('should infer for 4 arguments', () => {
const o = of('foo').pipe(a('1'), a('2'), a('3'), a('4')); // $ExpectType Observable<"4">
});

it('should infer for 5 arguments', () => {
const o = of('foo').pipe(a('1'), a('2'), a('3'), a('4'), a('5')); // $ExpectType Observable<"5">
});

it('should infer for 6 arguments', () => {
const o = of('foo').pipe(a('1'), a('2'), a('3'), a('4'), a('5'), a('6')); // $ExpectType Observable<"6">
});

it('should infer for 7 arguments', () => {
const o = of('foo').pipe(a('1'), a('2'), a('3'), a('4'), a('5'), a('6'), a('7')); // $ExpectType Observable<"7">
});

it('should infer for 8 arguments', () => {
const o = of('foo').pipe(a('1'), a('2'), a('3'), a('4'), a('5'), a('6'), a('7'), a('8')); // $ExpectType Observable<"8">
});

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

it('should infer {} for more than 9 arguments', () => {
const o = of('foo').pipe(a('1'), a('2'), a('3'), a('4'), a('5'), a('6'), a('7'), a('8'), a('9'), a('10')); // $ExpectType Observable<{}>
});

it('should require a type assertion for more than 9 arguments', () => {
const o: Observable<'10'> = of('foo').pipe(a('1'), a('2'), a('3'), a('4'), a('5'), a('6'), a('7'), a('8'), a('9'), a('10')); // $ExpectError
});

it('should enforce types for the 1st argument', () => {
const o = of('foo').pipe(a('#', '1')); // $ExpectError
});

it('should enforce types for the 2nd argument', () => {
const o = of('foo').pipe(a('1'), a('#', '2')); // $ExpectError
});

it('should enforce types for the 3rd argument', () => {
const o = of('foo').pipe(a('1'), a('2'), a('#', '3')); // $ExpectError
});

it('should enforce types for the 4th argument', () => {
const o = of('foo').pipe(a('1'), a('2'), a('3'), a('#', '4')); // $ExpectError
});

it('should enforce types for the 5th argument', () => {
const o = of('foo').pipe(a('1'), a('2'), a('3'), a('4'), a('#', '5')); // $ExpectError
});

it('should enforce types for the 6th argument', () => {
const o = of('foo').pipe(a('1'), a('2'), a('3'), a('4'), a('5'), a('#', '6')); // $ExpectError
});

it('should enforce types for the 7th argument', () => {
const o = of('foo').pipe(a('1'), a('2'), a('3'), a('4'), a('5'), a('6'), a('#', '7')); // $ExpectError
});

it('should enforce types for the 8th argument', () => {
const o = of('foo').pipe(a('1'), a('2'), a('3'), a('4'), a('5'), a('6'), a('7'), a('#', '8')); // $ExpectError
});

it('should enforce types for the 9th argument', () => {
const o = of('foo').pipe(a('1'), a('2'), a('3'), a('4'), a('5'), a('6'), a('7'), a('8'), a('#', '9')); // $ExpectError
});

it('should not enforce types beyond the 9th argument', () => {
const o = of('foo').pipe(a('1'), a('2'), a('3'), a('4'), a('5'), a('6'), a('7'), a('8'), a('9'), a('#', '10')); // $ExpectType Observable<{}>
});

it('should support operators that return generics', () => {
const customOperator = () => <T>(a: Observable<T>) => a;
const o = of('foo').pipe(customOperator()); // $ExpectType Observable<string>
});
});
77 changes: 77 additions & 0 deletions spec-dtslint/util/pipe-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { pipe, UnaryFunction } from 'rxjs';

function a<I extends string, O extends string>(input: I, output: O): UnaryFunction<I, O> {
return i => output;
}

it('should infer {} for no arguments', () => {
const o = pipe(); // $ExpectType UnaryFunction<{}, {}>
});

it('should infer for 1 argument', () => {
const o = pipe(a('0', '1')); // $ExpectType UnaryFunction<"0", "1">
});

it('should infer for 2 arguments', () => {
const o = pipe(a('0', '1'), a('1', '2')); // $ExpectType UnaryFunction<"0", "2">
});

it('should infer for 3 arguments', () => {
const o = pipe(a('0', '1'), a('1', '2'), a('2', '3')); // $ExpectType UnaryFunction<"0", "3">
});

it('should infer for 4 arguments', () => {
const o = pipe(a('0', '1'), a('1', '2'), a('2', '3'), a('3', '4')); // $ExpectType UnaryFunction<"0", "4">
});

it('should infer for 5 arguments', () => {
const o = pipe(a('0', '1'), a('1', '2'), a('2', '3'), a('3', '4'), a('4', '5')); // $ExpectType UnaryFunction<"0", "5">
});

it('should infer for 6 arguments', () => {
const o = pipe(a('0', '1'), a('1', '2'), a('2', '3'), a('3', '4'), a('4', '5'), a('5', '6')); // $ExpectType UnaryFunction<"0", "6">
});

it('should infer for 7 arguments', () => {
const o = pipe(a('0', '1'), a('1', '2'), a('2', '3'), a('3', '4'), a('4', '5'), a('5', '6'), a('6', '7')); // $ExpectType UnaryFunction<"0", "7">
});

it('should infer for 8 arguments', () => {
const o = pipe(a('0', '1'), a('1', '2'), a('2', '3'), a('3', '4'), a('4', '5'), a('5', '6'), a('6', '7'), a('7', '8')); // $ExpectType UnaryFunction<"0", "8">
});

it('should infer for 9 arguments', () => {
const o = pipe(a('0', '1'), a('1', '2'), a('2', '3'), a('3', '4'), a('4', '5'), a('5', '6'), a('6', '7'), a('7', '8'), a('8', '9')); // $ExpectType UnaryFunction<"0", "9">
});

it('should enforce types for the 2nd argument', () => {
const o = pipe(a('0', '1'), a('#', '2')); // $ExpectError
});

it('should enforce types for the 3rd argument', () => {
const o = pipe(a('0', '1'), a('1', '2'), a('#', '3')); // $ExpectError
});

it('should enforce types for the 4th argument', () => {
const o = pipe(a('0', '1'), a('1', '2'), a('2', '3'), a('#', '4')); // $ExpectError
});

it('should enforce types for the 5th argument', () => {
const o = pipe(a('0', '1'), a('1', '2'), a('2', '3'), a('3', '4'), a('#', '5')); // $ExpectError
});

it('should enforce types for the 6th argument', () => {
const o = pipe(a('0', '1'), a('1', '2'), a('2', '3'), a('3', '4'), a('4', '5'), a('#', '6')); // $ExpectError
});

it('should enforce types for the 7th argument', () => {
const o = pipe(a('0', '1'), a('1', '2'), a('2', '3'), a('3', '4'), a('4', '5'), a('5', '6'), a('#', '7')); // $ExpectError
});

it('should enforce types for the 8th argument', () => {
const o = pipe(a('0', '1'), a('1', '2'), a('2', '3'), a('3', '4'), a('4', '5'), a('5', '6'), a('6', '7'), a('#', '8')); // $ExpectError
});

it('should enforce types for the 9th argument', () => {
const o = pipe(a('0', '1'), a('1', '2'), a('2', '3'), a('3', '4'), a('4', '5'), a('5', '6'), a('6', '7'), a('7', '8'), a('#', '9')); // $ExpectError
});
2 changes: 1 addition & 1 deletion spec/operators/first-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ describe('Observable.prototype.first', () => {
const e1 = hot('--a-^--b--c--a--c--|');
const expected = '---------------(d|)';
const sub = '^ !';
expectObservable(e1.pipe(first(x => x === 's', 'd'))).toBe(expected);
expectObservable(e1.pipe(first<string>(x => x === 's', 'd'))).toBe(expected);
expectSubscriptions(e1.subscriptions).toBe(sub);
});

Expand Down
8 changes: 4 additions & 4 deletions spec/operators/reduce-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ describe('reduce operator', () => {
throw 'error';
};

expectObservable(e1.pipe(reduce(reduceFunction, seed))).toBe(expected);
expectObservable(e1.pipe(reduce<string>(reduceFunction, seed))).toBe(expected);
expectSubscriptions(e1.subscriptions).toBe(e1subs);
});

Expand Down Expand Up @@ -292,7 +292,7 @@ describe('reduce operator', () => {

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

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

type('should accept R typed reduces when R is an array of T', () => {
let a: Observable<number>;
const reduced = a.pipe(reduce((acc, value) => {
const reduced = a.pipe(reduce<number>((acc, value) => {
acc.push(value);
return acc;
}, []));
Expand Down Expand Up @@ -380,7 +380,7 @@ describe('reduce operator', () => {

type('should accept array of R typed reducers and reduce to array of R', () => {
let a: Observable<number>;
const reduced = a.pipe(reduce((acc, cur) => {
const reduced = a.pipe(reduce<number, string[]>((acc, cur) => {
console.log(acc);
acc.push(cur.toString());
return acc;
Expand Down
2 changes: 1 addition & 1 deletion spec/operators/scan-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ describe('scan operator', () => {

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

type('should accept T typed reducers', () => {
Expand Down
2 changes: 1 addition & 1 deletion spec/operators/startWith-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ describe('startWith operator', () => {
const e1subs = '^ !';
const expected = '-a-|';

expectObservable(e1.pipe(startWith(rxTestScheduler))).toBe(expected);
expectObservable(e1.pipe(startWith<any>(rxTestScheduler))).toBe(expected);
expectSubscriptions(e1.subscriptions).toBe(e1subs);
});

Expand Down
12 changes: 6 additions & 6 deletions spec/operators/zipAll-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ describe('zipAll operator', () => {
z: ['d', 3]
};

expectObservable(of(e1, myIterator).pipe(zipAll())).toBe(expected, values);
expectObservable(of(e1, myIterator).pipe(zipAll<string | number>())).toBe(expected, values);
expectSubscriptions(e1.subscriptions).toBe(e1subs);
});

Expand Down Expand Up @@ -160,7 +160,7 @@ describe('zipAll operator', () => {
const b = [1];
const expected = '|';

expectObservable(of(a, b).pipe(zipAll())).toBe(expected);
expectObservable(of(a, b).pipe(zipAll<string | number>())).toBe(expected);
expectSubscriptions(a.subscriptions).toBe(asubs);
});

Expand All @@ -180,7 +180,7 @@ describe('zipAll operator', () => {
const b = [1];
const expected = '-';

expectObservable(of(a, b).pipe(zipAll())).toBe(expected);
expectObservable(of(a, b).pipe(zipAll<string | number>())).toBe(expected);
expectSubscriptions(a.subscriptions).toBe(asubs);
});

Expand All @@ -190,7 +190,7 @@ describe('zipAll operator', () => {
const b = [2];
const expected = '-----(x|)';

expectObservable(of(a, b).pipe(zipAll())).toBe(expected, { x: ['1', 2] });
expectObservable(of(a, b).pipe(zipAll<string | number>())).toBe(expected, { x: ['1', 2] });
expectSubscriptions(a.subscriptions).toBe(asubs);
});

Expand All @@ -210,7 +210,7 @@ describe('zipAll operator', () => {
const b = [1];
const expected = '-----#';

expectObservable(of(a, b).pipe(zipAll())).toBe(expected);
expectObservable(of(a, b).pipe(zipAll<string | number>())).toBe(expected);
expectSubscriptions(a.subscriptions).toBe(asubs);
});

Expand All @@ -220,7 +220,7 @@ describe('zipAll operator', () => {
const b = [4, 5, 6];
const expected = '---x--y--(z|)';

expectObservable(of(a, b).pipe(zipAll())).toBe(expected,
expectObservable(of(a, b).pipe(zipAll<string | number>())).toBe(expected,
{ x: ['1', 4], y: ['2', 5], z: ['3', 6] });
expectSubscriptions(a.subscriptions).toBe(asubs);
});
Expand Down
4 changes: 2 additions & 2 deletions src/internal/Observable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ export class Observable<T> implements Subscribable<T> {
pipe<A, B, C, D, E, F, G>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>, op6: OperatorFunction<E, F>, op7: OperatorFunction<F, G>): Observable<G>;
pipe<A, B, C, D, E, F, G, H>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>, op6: OperatorFunction<E, F>, op7: OperatorFunction<F, G>, op8: OperatorFunction<G, H>): Observable<H>;
pipe<A, B, C, D, E, F, G, H, I>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>, op6: OperatorFunction<E, F>, op7: OperatorFunction<F, G>, op8: OperatorFunction<G, H>, op9: OperatorFunction<H, I>): Observable<I>;
pipe<R>(...operations: OperatorFunction<any, any>[]): Observable<R>;
pipe<A, B, C, D, E, F, G, H, I>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>, op6: OperatorFunction<E, F>, op7: OperatorFunction<F, G>, op8: OperatorFunction<G, H>, op9: OperatorFunction<H, I>, ...operations: OperatorFunction<any, any>[]): Observable<{}>;
/* tslint:enable:max-line-length */

/**
Expand All @@ -318,7 +318,7 @@ export class Observable<T> implements Subscribable<T> {
* .subscribe(x => console.log(x))
* ```
*/
pipe<R>(...operations: OperatorFunction<T, R>[]): Observable<R> {
pipe(...operations: OperatorFunction<any, any>[]): Observable<any> {
if (operations.length === 0) {
return this as any;
}
Expand Down

0 comments on commit 872b0ec

Please sign in to comment.