From 2d589726eb1c4797fc8b997dfc93b3c5bdef476d Mon Sep 17 00:00:00 2001 From: Nicholas Jamieson Date: Tue, 3 Oct 2017 10:23:43 +1000 Subject: [PATCH] fix(publish): fix selector typings Fixes the typings for publish and multicast, so that selectors that change the observable's type are supported. Closes #2889 --- spec/operators/multicast-spec.ts | 24 ++++++++++++++++++++++++ spec/operators/publish-spec.ts | 22 ++++++++++++++++++++++ src/operator/multicast.ts | 7 ++++--- src/operator/publish.ts | 5 +++-- src/operators/multicast.ts | 17 +++++++++-------- src/operators/publish.ts | 5 +++-- 6 files changed, 65 insertions(+), 15 deletions(-) diff --git a/spec/operators/multicast-spec.ts b/spec/operators/multicast-spec.ts index 15c65eaad5a..ca56dc38d30 100644 --- a/spec/operators/multicast-spec.ts +++ b/spec/operators/multicast-spec.ts @@ -3,6 +3,7 @@ import * as Rx from '../../dist/package/Rx'; import marbleTestingSignature = require('../helpers/marble-testing'); // tslint:disable-line:no-require-imports declare const { asDiagram, time, rxTestScheduler }; +declare const type; declare const hot: typeof marbleTestingSignature.hot; declare const cold: typeof marbleTestingSignature.cold; declare const expectObservable: typeof marbleTestingSignature.expectObservable; @@ -638,4 +639,27 @@ describe('Observable.prototype.multicast', () => { }); }); }); + + describe('typings', () => { + type('should infer the type', () => { + /* tslint:disable:no-unused-variable */ + const source = Rx.Observable.of(1, 2, 3); + const result: Rx.Observable = source.multicast(() => new Subject()); + /* tslint:enable:no-unused-variable */ + }); + + type('should infer the type with a selector', () => { + /* tslint:disable:no-unused-variable */ + const source = Rx.Observable.of(1, 2, 3); + const result: Rx.Observable = source.multicast(() => new Subject(), s => s.map(x => x)); + /* tslint:enable:no-unused-variable */ + }); + + type('should infer the type with a type-changing selector', () => { + /* tslint:disable:no-unused-variable */ + const source = Rx.Observable.of(1, 2, 3); + const result: Rx.Observable = source.multicast(() => new Subject(), s => s.map(x => x + '!')); + /* tslint:enable:no-unused-variable */ + }); + }); }); diff --git a/spec/operators/publish-spec.ts b/spec/operators/publish-spec.ts index e3d310e2400..b202502cef8 100644 --- a/spec/operators/publish-spec.ts +++ b/spec/operators/publish-spec.ts @@ -3,6 +3,7 @@ import * as Rx from '../../dist/package/Rx'; import marbleTestingSignature = require('../helpers/marble-testing'); // tslint:disable-line:no-require-imports declare const { asDiagram }; +declare const type; declare const hot: typeof marbleTestingSignature.hot; declare const cold: typeof marbleTestingSignature.cold; declare const expectObservable: typeof marbleTestingSignature.expectObservable; @@ -332,4 +333,25 @@ describe('Observable.prototype.publish', () => { expect(subscriptions).to.equal(1); done(); }); + + type('should infer the type', () => { + /* tslint:disable:no-unused-variable */ + const source = Rx.Observable.of(1, 2, 3); + const result: Rx.Observable = source.publish(); + /* tslint:enable:no-unused-variable */ + }); + + type('should infer the type with a selector', () => { + /* tslint:disable:no-unused-variable */ + const source = Rx.Observable.of(1, 2, 3); + const result: Rx.Observable = source.publish(s => s.map(x => x)); + /* tslint:enable:no-unused-variable */ + }); + + type('should infer the type with a type-changing selector', () => { + /* tslint:disable:no-unused-variable */ + const source = Rx.Observable.of(1, 2, 3); + const result: Rx.Observable = source.publish(s => s.map(x => x + '!')); + /* tslint:enable:no-unused-variable */ + }); }); diff --git a/src/operator/multicast.ts b/src/operator/multicast.ts index c511d50cec5..14c33010319 100644 --- a/src/operator/multicast.ts +++ b/src/operator/multicast.ts @@ -2,11 +2,12 @@ import { Subject } from '../Subject'; import { Observable } from '../Observable'; import { ConnectableObservable } from '../observable/ConnectableObservable'; import { multicast as higherOrder } from '../operators/multicast'; -import { FactoryOrValue, MonoTypeOperatorFunction } from '../interfaces'; +import { FactoryOrValue, MonoTypeOperatorFunction, OperatorFunction } from '../interfaces'; /* tslint:disable:max-line-length */ export function multicast(this: Observable, subjectOrSubjectFactory: FactoryOrValue>): ConnectableObservable; export function multicast(SubjectFactory: (this: Observable) => Subject, selector?: MonoTypeOperatorFunction): Observable; +export function multicast(SubjectFactory: (this: Observable) => Subject, selector?: OperatorFunction): Observable; /* tslint:enable:max-line-length */ /** @@ -103,7 +104,7 @@ export function multicast(SubjectFactory: (this: Observable) => Subject * @method multicast * @owner Observable */ -export function multicast(this: Observable, subjectOrSubjectFactory: Subject | (() => Subject), - selector?: (source: Observable) => Observable): Observable | ConnectableObservable { +export function multicast(this: Observable, subjectOrSubjectFactory: Subject | (() => Subject), + selector?: (source: Observable) => Observable): Observable | ConnectableObservable { return higherOrder(subjectOrSubjectFactory, selector)(this); } diff --git a/src/operator/publish.ts b/src/operator/publish.ts index 941330ebd7e..34157059dbe 100644 --- a/src/operator/publish.ts +++ b/src/operator/publish.ts @@ -5,7 +5,8 @@ import { publish as higherOrder } from '../operators/publish'; /* tslint:disable:max-line-length */ export function publish(this: Observable): ConnectableObservable; -export function publish(this: Observable, selector: selector): Observable; +export function publish(this: Observable, selector: (source: Observable) => Observable): Observable; +export function publish(this: Observable, selector: (source: Observable) => Observable): Observable; /* tslint:enable:max-line-length */ /** @@ -21,7 +22,7 @@ export function publish(this: Observable, selector: selector): Observab * @method publish * @owner Observable */ -export function publish(this: Observable, selector?: (source: Observable) => Observable): Observable | ConnectableObservable { +export function publish(this: Observable, selector?: (source: Observable) => Observable): Observable | ConnectableObservable { return higherOrder(selector)(this); } diff --git a/src/operators/multicast.ts b/src/operators/multicast.ts index 2c9666edefb..66effbcdd4f 100644 --- a/src/operators/multicast.ts +++ b/src/operators/multicast.ts @@ -3,11 +3,12 @@ import { Operator } from '../Operator'; import { Subscriber } from '../Subscriber'; import { Observable } from '../Observable'; import { ConnectableObservable, connectableObservableDescriptor } from '../observable/ConnectableObservable'; -import { FactoryOrValue, MonoTypeOperatorFunction } from '../interfaces'; +import { FactoryOrValue, MonoTypeOperatorFunction, OperatorFunction } from '../interfaces'; /* tslint:disable:max-line-length */ export function multicast(subjectOrSubjectFactory: FactoryOrValue>): MonoTypeOperatorFunction; export function multicast(SubjectFactory: (this: Observable) => Subject, selector?: MonoTypeOperatorFunction): MonoTypeOperatorFunction; +export function multicast(SubjectFactory: (this: Observable) => Subject, selector?: OperatorFunction): OperatorFunction; /* tslint:enable:max-line-length */ /** @@ -29,9 +30,9 @@ export function multicast(SubjectFactory: (this: Observable) => Subject * @method multicast * @owner Observable */ -export function multicast(subjectOrSubjectFactory: Subject | (() => Subject), - selector?: (source: Observable) => Observable): MonoTypeOperatorFunction { - return function multicastOperatorFunction(source: Observable): Observable { +export function multicast(subjectOrSubjectFactory: Subject | (() => Subject), + selector?: (source: Observable) => Observable): OperatorFunction { + return function multicastOperatorFunction(source: Observable): Observable { let subjectFactory: () => Subject; if (typeof subjectOrSubjectFactory === 'function') { subjectFactory = <() => Subject>subjectOrSubjectFactory; @@ -49,15 +50,15 @@ export function multicast(subjectOrSubjectFactory: Subject | (() => Subjec connectable.source = source; connectable.subjectFactory = subjectFactory; - return > connectable; + return > connectable; }; } -export class MulticastOperator implements Operator { +export class MulticastOperator implements Operator { constructor(private subjectFactory: () => Subject, - private selector: (source: Observable) => Observable) { + private selector: (source: Observable) => Observable) { } - call(subscriber: Subscriber, source: any): any { + call(subscriber: Subscriber, source: any): any { const { selector } = this; const subject = this.subjectFactory(); const subscription = selector(subject).subscribe(subscriber); diff --git a/src/operators/publish.ts b/src/operators/publish.ts index 95330bab94e..e9a9b123c2f 100644 --- a/src/operators/publish.ts +++ b/src/operators/publish.ts @@ -1,10 +1,11 @@ import { Subject } from '../Subject'; import { multicast } from './multicast'; -import { MonoTypeOperatorFunction } from '../interfaces'; +import { MonoTypeOperatorFunction, OperatorFunction } from '../interfaces'; /* tslint:disable:max-line-length */ export function publish(): MonoTypeOperatorFunction; export function publish(selector: MonoTypeOperatorFunction): MonoTypeOperatorFunction; +export function publish(selector: OperatorFunction): OperatorFunction; /* tslint:enable:max-line-length */ /** @@ -20,7 +21,7 @@ export function publish(selector: MonoTypeOperatorFunction): MonoTypeOpera * @method publish * @owner Observable */ -export function publish(selector?: MonoTypeOperatorFunction): MonoTypeOperatorFunction { +export function publish(selector?: any): any { return selector ? multicast(() => new Subject(), selector) : multicast(new Subject());