Skip to content

Commit

Permalink
fix(TS): fix type inference for publish variants
Browse files Browse the repository at this point in the history
- We can never get  out of  via TypeScript any how.
- Resolves a larger issue where operators with a single, static argument and a single  generic inferred too strongly from the argument and lost the additional type information from the source.
  • Loading branch information
benlesh committed Oct 15, 2019
1 parent dc89736 commit 5753dab
Show file tree
Hide file tree
Showing 9 changed files with 27 additions and 134 deletions.
9 changes: 7 additions & 2 deletions spec-dtslint/operators/publishBehavior-spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { of } from 'rxjs';
import { publishBehavior } from 'rxjs/operators';
import { publishBehavior, map } from 'rxjs/operators';

it('should enforce parameter', () => {
const a = of(1, 2, 3).pipe(publishBehavior()); // $ExpectError
Expand All @@ -10,5 +10,10 @@ it('should infer correctly with parameter', () => {
});

it('should enforce type on parameter', () => {
const a = of(1, 2, 3).pipe(publishBehavior('a'); // $ExpectError
const a = of(1, 2, 3).pipe(publishBehavior('a')); // $ExpectType Observable<string | number>
});

it('should compose properly', () => {
const fn = () => Math.random() > 0.5;
const a = of(true, false).pipe(map(x => x && fn()), publishBehavior(false)); // $ExpectType Observable<boolean>
});
45 changes: 0 additions & 45 deletions spec/operators/multicast-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -703,49 +703,4 @@ describe('multicast operator', () => {
});
});
});

describe('typings', () => {
type('should infer the type', () => {
/* tslint:disable:no-unused-variable */
const source = of(1, 2, 3);
const result: ConnectableObservable<number> = source.pipe(multicast(() => new Subject<number>())) as ConnectableObservable<number>;
/* tslint:enable:no-unused-variable */
});

type('should infer the type with a selector', () => {
/* tslint:disable:no-unused-variable */
const source = of(1, 2, 3);
const result: Observable<number> = source.pipe(multicast(() => new Subject<number>(), s => s.pipe(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 = of(1, 2, 3);
const result: Observable<string> = source.pipe(multicast(() => new Subject<number>(), s => s.pipe(map(x => x + '!'))));
/* tslint:enable:no-unused-variable */
});

type('should infer the type for the pipeable operator', () => {
/* tslint:disable:no-unused-variable */
const source = of(1, 2, 3);
// TODO: https://github.com/ReactiveX/rxjs/issues/2972
const result: ConnectableObservable<number> = multicast(() => new Subject<number>())(source);
/* tslint:enable:no-unused-variable */
});

type('should infer the type for the pipeable operator with a selector', () => {
/* tslint:disable:no-unused-variable */
const source = of(1, 2, 3);
const result: Observable<number> = source.pipe(multicast(() => new Subject<number>(), s => s.pipe(map(x => x))));
/* tslint:enable:no-unused-variable */
});

type('should infer the type for the pipeable operator with a type-changing selector', () => {
/* tslint:disable:no-unused-variable */
const source = of(1, 2, 3);
const result: Observable<string> = source.pipe(multicast(() => new Subject<number>(), s => s.pipe(map(x => x + '!'))));
/* tslint:enable:no-unused-variable */
});
});
});
43 changes: 0 additions & 43 deletions spec/operators/publish-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,47 +340,4 @@ describe('publish operator', () => {
expect(subscriptions).to.equal(1);
done();
});

type('should infer the type', () => {
/* tslint:disable:no-unused-variable */
const source = of(1, 2, 3);
const result: ConnectableObservable<number> = source.pipe(publish()) as ConnectableObservable<number>;
/* tslint:enable:no-unused-variable */
});

type('should infer the type with a selector', () => {
/* tslint:disable:no-unused-variable */
const source = of(1, 2, 3);
const result: Observable<number> = source.pipe(publish(s => s.pipe(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 = of(1, 2, 3);
const result: Observable<string> = source.pipe(publish(s => s.pipe(map(x => x + '!'))));
/* tslint:enable:no-unused-variable */
});

type('should infer the type for the pipeable operator', () => {
/* tslint:disable:no-unused-variable */
const source = of(1, 2, 3);
// TODO: https://github.com/ReactiveX/rxjs/issues/2972
const result: ConnectableObservable<number> = publish<number>()(source);
/* tslint:enable:no-unused-variable */
});

type('should infer the type for the pipeable operator with a selector', () => {
/* tslint:disable:no-unused-variable */
const source = of(1, 2, 3);
const result: Observable<number> = source.pipe(publish(s => s.pipe(map(x => x))));
/* tslint:enable:no-unused-variable */
});

type('should infer the type for the pipeable operator with a type-changing selector', () => {
/* tslint:disable:no-unused-variable */
const source = of(1, 2, 3);
const result: Observable<string> = source.pipe(publish(s => s.pipe(map(x => x + '!'))));
/* tslint:enable:no-unused-variable */
});
});
15 changes: 0 additions & 15 deletions spec/operators/publishBehavior-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,19 +347,4 @@ describe('publishBehavior operator', () => {
expect(results).to.deep.equal([]);
done();
});

type('should infer the type', () => {
/* tslint:disable:no-unused-variable */
const source = of(1, 2, 3);
const result: ConnectableObservable<number> = source.pipe(publishBehavior(0)) as ConnectableObservable<number>;
/* tslint:enable:no-unused-variable */
});

type('should infer the type for the pipeable operator', () => {
/* tslint:disable:no-unused-variable */
const source = of(1, 2, 3);
// TODO: https://github.com/ReactiveX/rxjs/issues/2972
const result: ConnectableObservable<number> = publishBehavior(0)(source);
/* tslint:enable:no-unused-variable */
});
});
15 changes: 0 additions & 15 deletions spec/operators/publishLast-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,19 +264,4 @@ describe('publishLast operator', () => {
expect(subscriptions).to.equal(1);
done();
});

type('should infer the type', () => {
/* tslint:disable:no-unused-variable */
const source = of(1, 2, 3);
const result: ConnectableObservable<number> = source.pipe(publishLast()) as ConnectableObservable<number>;
/* tslint:enable:no-unused-variable */
});

type('should infer the type for the pipeable operator', () => {
/* tslint:disable:no-unused-variable */
const source = of(1, 2, 3);
// TODO: https://github.com/ReactiveX/rxjs/issues/2972
const result: ConnectableObservable<{}> = publishLast()(source);
/* tslint:enable:no-unused-variable */
});
});
8 changes: 4 additions & 4 deletions src/internal/operators/multicast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { Operator } from '../Operator';
import { Subscriber } from '../Subscriber';
import { Observable } from '../Observable';
import { ConnectableObservable, connectableObservableDescriptor } from '../observable/ConnectableObservable';
import { OperatorFunction, UnaryFunction, ObservedValueOf, ObservableInput } from '../types';
import { OperatorFunction, UnaryFunction, ObservedValueOf, ObservableInput, MonoTypeOperatorFunction } from '../types';

/* tslint:disable:max-line-length */
export function multicast<T>(subject: Subject<T>): UnaryFunction<Observable<T>, ConnectableObservable<T>>;
export function multicast<T, O extends ObservableInput<any>>(subject: Subject<T>, selector: (shared: Observable<T>) => O): UnaryFunction<Observable<T>, ConnectableObservable<ObservedValueOf<O>>>;
export function multicast<T>(subjectFactory: (this: Observable<T>) => Subject<T>): UnaryFunction<Observable<T>, ConnectableObservable<T>>;
export function multicast<T>(subject: Subject<T>): MonoTypeOperatorFunction<T>;
export function multicast<T, O extends ObservableInput<any>>(subject: Subject<T>, selector: (shared: Observable<T>) => O): OperatorFunction<T, ObservedValueOf<O>>;
export function multicast<T>(subjectFactory: (this: Observable<T>) => Subject<T>): MonoTypeOperatorFunction<T>;
export function multicast<T, O extends ObservableInput<any>>(SubjectFactory: (this: Observable<T>) => Subject<T>, selector: (shared: Observable<T>) => O): OperatorFunction<T, ObservedValueOf<O>>;
/* tslint:enable:max-line-length */

Expand Down
2 changes: 1 addition & 1 deletion src/internal/operators/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ConnectableObservable } from '../observable/ConnectableObservable';
import { MonoTypeOperatorFunction, OperatorFunction, UnaryFunction, ObservableInput, ObservedValueOf } from '../types';

/* tslint:disable:max-line-length */
export function publish<T>(): UnaryFunction<Observable<T>, ConnectableObservable<T>>;
export function publish<T>(): MonoTypeOperatorFunction<T>;
export function publish<T, O extends ObservableInput<any>>(selector: (shared: Observable<T>) => O): OperatorFunction<T, ObservedValueOf<O>>;
export function publish<T>(selector: MonoTypeOperatorFunction<T>): MonoTypeOperatorFunction<T>;
/* tslint:enable:max-line-length */
Expand Down
20 changes: 13 additions & 7 deletions src/internal/operators/publishBehavior.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { Observable } from '../Observable';
import { BehaviorSubject } from '../BehaviorSubject';
import { multicast } from './multicast';
import { ConnectableObservable } from '../observable/ConnectableObservable';
import { UnaryFunction } from '../types';
import { OperatorFunction } from '../types';

/**
* @param value
* Multicasts the observable source through an underlying {@link BehaviorSubject}. All subscriptions
* to the resulting observable will subscribe to the underlaying `BehaviorSubject`, the
* resulting observable is a {@link ConnectableObservable}. If you call `connect()` on that observable
* (requires a cast to `ConnectableObservable` in TypeScript), it will subscribe to the source
* observable with the underlying subject and connect that source too all consumers subscribed through the
* subject. Because it's using a `BehaviorSubject`, all new subscriptions will get the most recent value
* that has passed through said subject, _or_ they will get the `initialValue` if no values have
* arrived yet, so long as the published `ConnectableObservable` has been connected.
*
* @param initialValue
* @return {ConnectableObservable<T>}
* @method publishBehavior
* @owner Observable
*/
export function publishBehavior<T>(value: T): UnaryFunction<Observable<T>, ConnectableObservable<T>> {
return (source: Observable<T>) => multicast(new BehaviorSubject<T>(value))(source) as ConnectableObservable<T>;
export function publishBehavior<T, D>(initialValue: D): OperatorFunction<T, T | D> {
return (source: Observable<T>) => multicast(new BehaviorSubject<T | D>(initialValue))(source);
}
4 changes: 2 additions & 2 deletions src/internal/operators/publishLast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Observable } from '../Observable';
import { AsyncSubject } from '../AsyncSubject';
import { multicast } from './multicast';
import { ConnectableObservable } from '../observable/ConnectableObservable';
import { UnaryFunction } from '../types';
import { UnaryFunction, MonoTypeOperatorFunction } from '../types';

/**
* Returns a connectable observable sequence that shares a single subscription to the
Expand Down Expand Up @@ -62,6 +62,6 @@ import { UnaryFunction } from '../types';
* @owner Observable
*/

export function publishLast<T>(): UnaryFunction<Observable<T>, ConnectableObservable<T>> {
export function publishLast<T>(): MonoTypeOperatorFunction<T> {
return (source: Observable<T>) => multicast(new AsyncSubject<T>())(source);
}

0 comments on commit 5753dab

Please sign in to comment.