From 04ec474cca35d0ce7bb8348472563fe59573c1c0 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Wed, 7 Oct 2020 17:37:16 -0500 Subject: [PATCH] fix: No longer allow invalid "Subscribable" type as valid observable source in `from` and others. - Deprecates `SubscribableOrPromise` type and removes its usage throughout the library - Updates `ObservableInput` to be a more direct list and include `Observable` itself. - Updates `switchAll` to have proper typing (it broke after the refactor of `ObservableInput`), removing weird legacy type - Updates related tests Resolves #4532 --- spec/operators/expand-spec.ts | 20 +++++++++----------- src/internal/observable/forkJoin.ts | 4 ++-- src/internal/observable/from.ts | 2 +- src/internal/observable/iif.ts | 6 +++--- src/internal/operators/audit.ts | 9 ++++----- src/internal/operators/bufferToggle.ts | 13 ++++++------- src/internal/operators/debounce.ts | 9 ++++----- src/internal/operators/switchAll.ts | 5 ++--- src/internal/operators/throttle.ts | 11 +++++------ src/internal/types.ts | 9 ++++++++- src/internal/util/argsArgArrayOrObject.ts | 2 +- 11 files changed, 45 insertions(+), 45 deletions(-) diff --git a/spec/operators/expand-spec.ts b/spec/operators/expand-spec.ts index 13ffa83df51..d2911311971 100644 --- a/spec/operators/expand-spec.ts +++ b/spec/operators/expand-spec.ts @@ -2,7 +2,7 @@ import { expect } from 'chai'; import { expand, mergeMap, map, take, toArray } from 'rxjs/operators'; import { TestScheduler } from 'rxjs/testing'; import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; -import { Subscribable, EMPTY, Observable, of, Observer, asapScheduler, asyncScheduler } from 'rxjs'; +import { Subscribable, EMPTY, Observable, of, Observer, asapScheduler, asyncScheduler, InteropObservable } from 'rxjs'; declare const rxTestScheduler: TestScheduler; @@ -359,22 +359,20 @@ describe('expand', () => { it('should recursively flatten lowercase-o observables', (done) => { const expected = [1, 2, 4, 8, 16]; - const project = (x: number, index: number): Subscribable => { + const project = (x: number): InteropObservable => { if (x === 16) { - return EMPTY; + return EMPTY as any; } - const ish: any = { - subscribe: (observer: Observer) => { + return { + subscribe(observer: Observer) { observer.next(x + x); observer.complete(); + }, + [Symbol.observable] () { + return this; } - }; - - ish[Symbol.observable] = function () { - return this; - }; - return > ish; + } as any; }; of(1).pipe( diff --git a/src/internal/observable/forkJoin.ts b/src/internal/observable/forkJoin.ts index 4e672b974b8..105fb847c8b 100644 --- a/src/internal/observable/forkJoin.ts +++ b/src/internal/observable/forkJoin.ts @@ -1,6 +1,6 @@ /** @prettier */ import { Observable } from '../Observable'; -import { ObservableInput, ObservedValueUnionFromArray, ObservedValueOf, SubscribableOrPromise } from '../types'; +import { ObservableInput, ObservedValueUnionFromArray, ObservedValueOf } from '../types'; import { map } from '../operators/map'; import { argsArgArrayOrObject } from '../util/argsArgArrayOrObject'; import { innerFrom } from './from'; @@ -8,7 +8,7 @@ import { popResultSelector } from '../util/args'; // forkJoin(a$, b$, c$) /** @deprecated Use the version that takes an array of Observables instead */ -export function forkJoin(v1: SubscribableOrPromise): Observable<[T]>; +export function forkJoin(v1: ObservableInput): Observable<[T]>; /** @deprecated Use the version that takes an array of Observables instead */ export function forkJoin(v1: ObservableInput, v2: ObservableInput): Observable<[T, T2]>; /** @deprecated Use the version that takes an array of Observables instead */ diff --git a/src/internal/observable/from.ts b/src/internal/observable/from.ts index 7afe5eef762..abc768bcf52 100644 --- a/src/internal/observable/from.ts +++ b/src/internal/observable/from.ts @@ -125,7 +125,7 @@ export function from(input: ObservableInput, scheduler?: SchedulerLike): O // TODO: Use this throughout the library, rather than the `from` above, to avoid // the unnecessary scheduling check and reduce bundled sizes of operators that use `from`. // TODO: Eventually, this just becomes `from`, as we don't have the deprecated scheduled path anymore. -export function innerFrom(input: ObservableInput) { +export function innerFrom(input: ObservableInput): Observable { if (input instanceof Observable) { return input; } diff --git a/src/internal/observable/iif.ts b/src/internal/observable/iif.ts index 661873921cf..552718d1486 100644 --- a/src/internal/observable/iif.ts +++ b/src/internal/observable/iif.ts @@ -1,7 +1,7 @@ import { Observable } from '../Observable'; import { defer } from './defer'; import { EMPTY } from './empty'; -import { SubscribableOrPromise } from '../types'; +import { ObservableInput } from '../types'; /** * Decides at subscription time which Observable will actually be subscribed. @@ -93,8 +93,8 @@ import { SubscribableOrPromise } from '../types'; */ export function iif( condition: () => boolean, - trueResult: SubscribableOrPromise = EMPTY, - falseResult: SubscribableOrPromise = EMPTY + trueResult: ObservableInput = EMPTY, + falseResult: ObservableInput = EMPTY ): Observable { return defer(() => condition() ? trueResult : falseResult); } diff --git a/src/internal/operators/audit.ts b/src/internal/operators/audit.ts index 31ef7a8f780..96e955459fb 100644 --- a/src/internal/operators/audit.ts +++ b/src/internal/operators/audit.ts @@ -1,6 +1,6 @@ /** @prettier */ import { Subscriber } from '../Subscriber'; -import { MonoTypeOperatorFunction, SubscribableOrPromise } from '../types'; +import { MonoTypeOperatorFunction, ObservableInput } from '../types'; import { operate } from '../util/lift'; import { innerFrom } from '../observable/from'; @@ -44,14 +44,13 @@ import { OperatorSubscriber } from './OperatorSubscriber'; * @see {@link sample} * @see {@link throttle} * - * @param {function(value: T): SubscribableOrPromise} durationSelector A function + * @param durationSelector A function * that receives a value from the source Observable, for computing the silencing * duration, returned as an Observable or a Promise. - * @return {Observable} An Observable that performs rate-limiting of + * @return An Observable that performs rate-limiting of * emissions from the source Observable. - * @name audit */ -export function audit(durationSelector: (value: T) => SubscribableOrPromise): MonoTypeOperatorFunction { +export function audit(durationSelector: (value: T) => ObservableInput): MonoTypeOperatorFunction { return operate((source, subscriber) => { let hasValue = false; let lastValue: T | null = null; diff --git a/src/internal/operators/bufferToggle.ts b/src/internal/operators/bufferToggle.ts index 786d7f2f05a..3ed839241c9 100644 --- a/src/internal/operators/bufferToggle.ts +++ b/src/internal/operators/bufferToggle.ts @@ -1,6 +1,6 @@ /** @prettier */ import { Subscription } from '../Subscription'; -import { OperatorFunction, SubscribableOrPromise } from '../types'; +import { OperatorFunction, ObservableInput } from '../types'; import { operate } from '../util/lift'; import { innerFrom } from '../observable/from'; import { OperatorSubscriber } from './OperatorSubscriber'; @@ -43,18 +43,17 @@ import { arrRemove } from '../util/arrRemove'; * @see {@link bufferWhen} * @see {@link windowToggle} * - * @param {SubscribableOrPromise} openings A Subscribable or Promise of notifications to start new + * @param openings A Subscribable or Promise of notifications to start new * buffers. - * @param {function(value: O): SubscribableOrPromise} closingSelector A function that takes + * @param closingSelector A function that takes * the value emitted by the `openings` observable and returns a Subscribable or Promise, * which, when it emits, signals that the associated buffer should be emitted * and cleared. - * @return {Observable} An observable of arrays of buffered values. - * @name bufferToggle + * @return An observable of arrays of buffered values. */ export function bufferToggle( - openings: SubscribableOrPromise, - closingSelector: (value: O) => SubscribableOrPromise + openings: ObservableInput, + closingSelector: (value: O) => ObservableInput ): OperatorFunction { return operate((source, subscriber) => { const buffers: T[][] = []; diff --git a/src/internal/operators/debounce.ts b/src/internal/operators/debounce.ts index b360a3adc8f..4d6e5b23fbd 100644 --- a/src/internal/operators/debounce.ts +++ b/src/internal/operators/debounce.ts @@ -1,6 +1,6 @@ /** @prettier */ import { Subscriber } from '../Subscriber'; -import { MonoTypeOperatorFunction, SubscribableOrPromise } from '../types'; +import { MonoTypeOperatorFunction, ObservableInput } from '../types'; import { operate } from '../util/lift'; import { OperatorSubscriber } from './OperatorSubscriber'; @@ -56,15 +56,14 @@ import { innerFrom } from '../observable/from'; * @see {@link throttle} * @see {@link throttleTime} * - * @param {function(value: T): SubscribableOrPromise} durationSelector A function + * @param durationSelector A function * that receives a value from the source Observable, for computing the timeout * duration for each source value, returned as an Observable or a Promise. - * @return {Observable} An Observable that delays the emissions of the source + * @return An Observable that delays the emissions of the source * Observable by the specified duration Observable returned by * `durationSelector`, and may drop some values if they occur too frequently. - * @name debounce */ -export function debounce(durationSelector: (value: T) => SubscribableOrPromise): MonoTypeOperatorFunction { +export function debounce(durationSelector: (value: T) => ObservableInput): MonoTypeOperatorFunction { return operate((source, subscriber) => { let hasValue = false; let lastValue: T | null = null; diff --git a/src/internal/operators/switchAll.ts b/src/internal/operators/switchAll.ts index 1334c4f5509..08ea1635982 100644 --- a/src/internal/operators/switchAll.ts +++ b/src/internal/operators/switchAll.ts @@ -1,9 +1,8 @@ -import {OperatorFunction, ObservableInput} from '../types'; +import {OperatorFunction, ObservableInput, ObservedValueOf} from '../types'; import { switchMap } from './switchMap'; import { identity } from '../util/identity'; -export function switchAll(): OperatorFunction, T>; -export function switchAll(): OperatorFunction; +export function switchAll>(): OperatorFunction>; /** * Converts a higher-order Observable into a first-order Observable diff --git a/src/internal/operators/throttle.ts b/src/internal/operators/throttle.ts index 676d19b5567..9dff66248ec 100644 --- a/src/internal/operators/throttle.ts +++ b/src/internal/operators/throttle.ts @@ -1,7 +1,7 @@ /** @prettier */ import { Subscription } from '../Subscription'; -import { MonoTypeOperatorFunction, SubscribableOrPromise } from '../types'; +import { MonoTypeOperatorFunction, ObservableInput } from '../types'; import { operate } from '../util/lift'; import { OperatorSubscriber } from './OperatorSubscriber'; import { innerFrom } from '../observable/from'; @@ -52,17 +52,16 @@ export const defaultThrottleConfig: ThrottleConfig = { * @see {@link sample} * @see {@link throttleTime} * - * @param {function(value: T): SubscribableOrPromise} durationSelector A function + * @param durationSelector A function * that receives a value from the source Observable, for computing the silencing * duration for each source value, returned as an Observable or a Promise. - * @param {Object} config a configuration object to define `leading` and `trailing` behavior. Defaults + * @param config a configuration object to define `leading` and `trailing` behavior. Defaults * to `{ leading: true, trailing: false }`. - * @return {Observable} An Observable that performs the throttle operation to + * @return An Observable that performs the throttle operation to * limit the rate of emissions from the source. - * @name throttle */ export function throttle( - durationSelector: (value: T) => SubscribableOrPromise, + durationSelector: (value: T) => ObservableInput, { leading, trailing }: ThrottleConfig = defaultThrottleConfig ): MonoTypeOperatorFunction { return operate((source, subscriber) => { diff --git a/src/internal/types.ts b/src/internal/types.ts index 8e685c1b41b..1e0505b0149 100644 --- a/src/internal/types.ts +++ b/src/internal/types.ts @@ -61,6 +61,7 @@ export interface SubscriptionLike extends Unsubscribable { readonly closed: boolean; } +/** @deprecated To be removed in v8. Do not use. Most likely you want to use `ObservableInput` */ export type SubscribableOrPromise = Subscribable | Subscribable | PromiseLike | InteropObservable; /** OBSERVABLE INTERFACES */ @@ -76,11 +77,17 @@ export interface Subscribable { subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): Unsubscribable; } -export type ObservableInput = SubscribableOrPromise | ArrayLike | Iterable | AsyncIterableIterator; +/** + * Valid types that can be converted to observables. + */ +export type ObservableInput = Observable | InteropObservable | AsyncIterable | PromiseLike | ArrayLike | Iterable; /** @deprecated use {@link InteropObservable } */ export type ObservableLike = InteropObservable; +/** + * An object that implements the `Symbol.observable` interface. + */ export interface InteropObservable { [Symbol.observable]: () => Subscribable; } diff --git a/src/internal/util/argsArgArrayOrObject.ts b/src/internal/util/argsArgArrayOrObject.ts index 3966f944923..764fd6548bd 100644 --- a/src/internal/util/argsArgArrayOrObject.ts +++ b/src/internal/util/argsArgArrayOrObject.ts @@ -18,7 +18,7 @@ export function argsArgArrayOrObject>(args: T[] | if (isPOJO(first)) { const keys = getKeys(first); return { - args: keys.map((key) => (first as O)[key]), + args: keys.map((key) => first[key]), keys, }; }