From d652b6a900b420f90e28bbe57606a21b6a30460d Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Fri, 27 Oct 2023 11:41:55 -0500 Subject: [PATCH] feat: remove `Symbol.observable` export (#4466) BREAKING CHANGE: `observable` (the `Symbol.observable` symbol instance) is no longer exported. Use a polyfill like `symbol-observable`, or use `Symbol.observable ?? '@@observable'` as a workaround. --- .../rxjs/spec/helpers/interop-helper-spec.ts | 3 +- packages/rxjs/spec/helpers/test-helper.ts | 57 +++++++++---------- packages/rxjs/spec/observables/from-spec.ts | 8 +-- packages/rxjs/src/index.ts | 1 - packages/rxjs/src/internal/Observable.ts | 3 +- packages/rxjs/src/internal/observable/from.ts | 3 +- .../rxjs/src/internal/symbol/observable.ts | 7 --- .../src/internal/util/isInteropObservable.ts | 3 +- 8 files changed, 36 insertions(+), 49 deletions(-) delete mode 100644 packages/rxjs/src/internal/symbol/observable.ts diff --git a/packages/rxjs/spec/helpers/interop-helper-spec.ts b/packages/rxjs/spec/helpers/interop-helper-spec.ts index a4b061d686..360977dfca 100644 --- a/packages/rxjs/spec/helpers/interop-helper-spec.ts +++ b/packages/rxjs/spec/helpers/interop-helper-spec.ts @@ -1,13 +1,12 @@ import { expect } from 'chai'; import { Observable, of, Subscriber } from 'rxjs'; -import { observable as symbolObservable } from 'rxjs/internal/symbol/observable'; import { asInteropObservable, asInteropSubscriber } from './interop-helper'; describe('interop helper', () => { it('should simulate interop observables', () => { const observable: any = asInteropObservable(of(42)); expect(observable).to.not.be.instanceOf(Observable); - expect(observable[symbolObservable]).to.be.a('function'); + expect(observable[Symbol.observable ?? '@@observable']).to.be.a('function'); }); it('should simulate interop subscribers', () => { diff --git a/packages/rxjs/spec/helpers/test-helper.ts b/packages/rxjs/spec/helpers/test-helper.ts index 0780fa1639..3abd7769ed 100644 --- a/packages/rxjs/spec/helpers/test-helper.ts +++ b/packages/rxjs/spec/helpers/test-helper.ts @@ -1,5 +1,4 @@ import { of, asyncScheduler, Observable, scheduled, ObservableInput } from 'rxjs'; -import { observable } from 'rxjs/internal/symbol/observable'; import { iterator } from 'rxjs/internal/symbol/iterator'; if (process && process.on) { @@ -9,7 +8,7 @@ if (process && process.on) { * it handles the rejected promise where it does not notice * that the test failed. */ - process.on('unhandledRejection', err => { + process.on('unhandledRejection', (err) => { console.error(err); process.exit(1); }); @@ -18,45 +17,45 @@ if (process && process.on) { export function lowerCaseO(...args: Array): Observable { const o: any = { subscribe(observer: any) { - args.forEach(v => observer.next(v)); + args.forEach((v) => observer.next(v)); observer.complete(); return { - unsubscribe() { /* do nothing */ } + unsubscribe() { + /* do nothing */ + }, }; - } + }, }; - o[observable] = function (this: any) { + o[Symbol.observable ?? '@@observable'] = function (this: any) { return this; }; return o; } -export const createObservableInputs = (value: T) => of( - of(value), - scheduled([value], asyncScheduler), - [value], - Promise.resolve(value), - { - [iterator]: () => { - const iteratorResults = [ - { value, done: false }, - { done: true } - ]; - return { - next: () => { - return iteratorResults.shift(); - } - }; - } - } as any as Iterable, - { - [observable]: () => of(value) - } as any -) as Observable>; +export const createObservableInputs = (value: T) => + of( + of(value), + scheduled([value], asyncScheduler), + [value], + Promise.resolve(value), + { + [iterator]: () => { + const iteratorResults = [{ value, done: false }, { done: true }]; + return { + next: () => { + return iteratorResults.shift(); + }, + }; + }, + } as any as Iterable, + { + [Symbol.observable ?? '@@observable']: () => of(value), + } as any + ) as Observable>; /** * Used to signify no subscriptions took place to `expectSubscriptions` assertions. */ -export const NO_SUBS: string[] = []; \ No newline at end of file +export const NO_SUBS: string[] = []; diff --git a/packages/rxjs/spec/observables/from-spec.ts b/packages/rxjs/spec/observables/from-spec.ts index e51a97891c..03ee73005e 100644 --- a/packages/rxjs/spec/observables/from-spec.ts +++ b/packages/rxjs/spec/observables/from-spec.ts @@ -1,7 +1,7 @@ /** @prettier */ import { expect } from 'chai'; import { TestScheduler } from 'rxjs/testing'; -import { asyncScheduler, of, from, Observer, observable, Subject, noop, Subscription } from 'rxjs'; +import { of, from, Observer, Subject, noop, Subscription } from 'rxjs'; import { first, concatMap, delay, take, tap } from 'rxjs/operators'; import { ReadableStream } from 'web-streams-polyfill'; import { observableMatcher } from '../helpers/observableMatcher'; @@ -166,7 +166,7 @@ describe('from', () => { }); const fakervable = (...values: T[]) => ({ - [observable]: () => ({ + [Symbol.observable ?? '@@observable']: () => ({ subscribe: (observer: Observer) => { for (const value of values) { observer.next(value); @@ -178,7 +178,7 @@ describe('from', () => { const fakeArrayObservable = (...values: T[]) => { let arr: any = ['bad array!']; - arr[observable] = () => { + arr[Symbol.observable ?? '@@observable'] = () => { return { subscribe: (observer: Observer) => { for (const value of values) { @@ -277,7 +277,7 @@ describe('from', () => { it(`should accept a function that implements [Symbol.observable]`, (done) => { const subject = new Subject(); const handler: any = (arg: any) => subject.next(arg); - handler[observable] = () => subject; + handler[Symbol.observable ?? '@@observable'] = () => subject; let nextInvoked = false; from(handler as any) diff --git a/packages/rxjs/src/index.ts b/packages/rxjs/src/index.ts index f2a0b573d3..ab9984875a 100644 --- a/packages/rxjs/src/index.ts +++ b/packages/rxjs/src/index.ts @@ -16,7 +16,6 @@ export { Observable } from './internal/Observable.js'; export { GroupedObservable } from './internal/operators/groupBy.js'; export { Operator } from './internal/Operator.js'; -export { observable } from './internal/symbol/observable.js'; export { animationFrames } from './internal/observable/dom/animationFrames.js'; /* Subjects */ diff --git a/packages/rxjs/src/internal/Observable.ts b/packages/rxjs/src/internal/Observable.ts index b633d82b54..685bdd1dca 100644 --- a/packages/rxjs/src/internal/Observable.ts +++ b/packages/rxjs/src/internal/Observable.ts @@ -1,7 +1,6 @@ import { Subscriber } from './Subscriber.js'; import { Subscription } from './Subscription.js'; import { TeardownLogic, UnaryFunction, Subscribable, Observer, OperatorFunction } from './types.js'; -import { observable as Symbol_observable } from './symbol/observable.js'; import { pipeFromArray } from './util/pipe.js'; /** * A representation of any set of values over any amount of time. This is the most basic building block @@ -240,7 +239,7 @@ export class Observable implements Subscribable { * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable * @return This instance of the observable. */ - [Symbol_observable]() { + [Symbol.observable ?? '@@observable']() { return this; } diff --git a/packages/rxjs/src/internal/observable/from.ts b/packages/rxjs/src/internal/observable/from.ts index 96e4d23d71..678952a808 100644 --- a/packages/rxjs/src/internal/observable/from.ts +++ b/packages/rxjs/src/internal/observable/from.ts @@ -10,7 +10,6 @@ import { isReadableStreamLike, readableStreamLikeToAsyncGenerator } from '../uti import { Subscriber } from '../Subscriber.js'; import { isFunction } from '../util/isFunction.js'; import { reportUnhandledError } from '../util/reportUnhandledError.js'; -import { observable as Symbol_observable } from '../symbol/observable.js'; /** * Creates an Observable from an Array, an array-like object, a Promise, an iterable object, or an Observable-like object. @@ -117,7 +116,7 @@ export function from(input: ObservableInput): Observable { */ function fromInteropObservable(obj: any) { return new Observable((subscriber: Subscriber) => { - const obs = obj[Symbol_observable](); + const obs = obj[Symbol.observable ?? '@@observable'](); if (isFunction(obs.subscribe)) { return obs.subscribe(subscriber); } diff --git a/packages/rxjs/src/internal/symbol/observable.ts b/packages/rxjs/src/internal/symbol/observable.ts deleted file mode 100644 index b133245c5c..0000000000 --- a/packages/rxjs/src/internal/symbol/observable.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Symbol.observable or a string "@@observable". Used for interop - * - * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS. - * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable - */ -export const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')(); diff --git a/packages/rxjs/src/internal/util/isInteropObservable.ts b/packages/rxjs/src/internal/util/isInteropObservable.ts index eaf04b79de..f4bde0ffc0 100644 --- a/packages/rxjs/src/internal/util/isInteropObservable.ts +++ b/packages/rxjs/src/internal/util/isInteropObservable.ts @@ -1,8 +1,7 @@ import { InteropObservable } from '../types.js'; -import { observable as Symbol_observable } from '../symbol/observable.js'; import { isFunction } from './isFunction.js'; /** Identifies an input as being Observable (but not necessary an Rx Observable) */ export function isInteropObservable(input: any): input is InteropObservable { - return isFunction(input[Symbol_observable]); + return isFunction(input[Symbol.observable ?? '@@observable']); }