Rxjs: RxJS incorrectly defines observable symbol

Created on 10 Dec 2018  路  9Comments  路  Source: ReactiveX/rxjs

Bug Report

It would appear that the current way RxJS detects other observables is not working as expected, while other libraries can adapt from each other.

Current Behavior
The from function in RxJS 6.3.3 does not seem able to adapt xstream and most while the older RxJS 5 Observable.from was able to. The following error is reproduced in both cases:

TypeError: You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.

Both xstream and most provide Symbol polyfills:

Additional testing has confirmed:

  • This issue does not occur in webpack but it does in node, jest, and browserify.
  • The order of import seems to matter.

Reproduction

  • From xstream to rxjs, does not work:
const { from: observableFrom } = require('rxjs')
const { isInteropObservable } = require('rxjs/internal/util/isInteropObservable')
const { default: xs } = require('xstream')

const num$ = xs.from([ 1, 2, 3, 4 ])
console.log('Is num$ an observable?', isInteropObservable(num$)) // false

const adapt$ = observableFrom(num$) // error here
adapt$.subscribe(console.log)
  • From xstream to rxjs, working after importing xstream first:
const { default: xs } = require('xstream')
const { from: observableFrom } = require('rxjs')
const { isInteropObservable } = require('rxjs/internal/util/isInteropObservable')

const num$ = xs.from([ 1, 2, 3, 4 ])
console.log('Is num$ an observable?', isInteropObservable(num$)) // true

const adapt$ = observableFrom(num$)
adapt$.subscribe(console.log)
  • The same behaviour in most, not working:
const { from: observableFrom } = require('rxjs')
const { isInteropObservable } = require('rxjs/internal/util/isInteropObservable')
const most = require('most')

const num$ = most.from([ 1, 2, 3, 4 ])
console.log('Is num$ an observable?', isInteropObservable(num$)) // false

const adapt$ = observableFrom(num$) // error here
adapt$.subscribe(console.log)
  • We cannot convert rxjs to xstream either unless we import xstream first:
const { from: observableFrom } = require('rxjs')
const { default: xs } = require('xstream')

const num$ = observableFrom([ 1, 2, 3, 4 ])
const adapt$ = xs.from(num$)

adapt$.addListener({
  next: console.log
})

Expected behavior
The order of import should not matter, and the jest environment would work like webpack.

Environment

  • Runtime: Node v11.3.0
  • RxJS version: 6.3.3
  • xstream version: 11.7.0
  • most version: 1.7.3

Possible Solution

  • The file rxjs/src/internal/symbol/observable.ts could be defined the same way the other libraries do it:
Symbol('observable')
  • The isInteropObservable could be patched to check for the same symbol xstream and most use?
  • A similar polyfill to xstream or most could be included?

Additional context/Screenshots

  • This issue came about while working on a cyclejs project with rxjs.
  • Adding the following to jest.setup.js seems to have fixed the issue for now:
import xs from 'xstream' // eslint-disable-line

Thank you and sorry in advance if I've diagnosed the wrong source :bowing_man:

bug

All 9 comments

Need some digging, esp why our interop utility doesn't work based on import order.

couple of clarifications meanwhile

could be patched to check for the same symbol xstream and most use?

I think we already do, need to figure out why it doesn't work.

A similar polyfill to xstream or most could be included?

from v6, we intentionally removed polyfilling symbol for our own but only check if symbol exists.

Need some digging, esp why our interop utility doesn't work based on import order.

If RxJS loads and there is no Symbol.observable, the string '@@observable' is used instead.

If xstream loads and there is no Symbol.observable, it assigns Symbol('observable') to Symbol.observable. xstream only uses '@@observable' if Symbol does not exist.

Don't we revalidate existense of symbol each time time when fn called? I thought we did that.

Uh welp. :sweat_smile:

Any updates on this? I faced this issue with refract-rxjs which revalidates existence of symbol. It brought a hard to track bug that started to occur when I reordered my imports.

I'm experiencing this too, unable to convert xstream streams to rxjs streams.

Digging into this:

It looks like the issue is we're not using unique symbol types with our typings. Furthermore, everyone else is using symbol-observable, which we could probably use as well, after we update it to _also_ use unique symbol in it's types.

https://github.com/benlesh/symbol-observable/issues/41

In the end, I'm not sure what we should do here to fix the typings though. It seems like once something is imported from another module or otherwise stuffed in a variable, TypeScript gets rather confused about what works and what doesn't.

There is another issue in the original post here, which is that, really, polyfills should always be applied _before_ any libraries are loaded. So the runtime fix would be to just ensure the polyfilling happened first before the other imports.

The TypeScript issue I'm not sure we can fix at the moment.

I'm closing this for now, but I'm willing to reopen this later if this becomes a major issue.

This is related to issue #3890, and there's a solid workaround for the typings issue

The runtime issue can be resolved with a simple polyfill or just changing the order of imports.

// Running this code before any of your imports will resolve any runtime issues.
if (typeof Symbol === 'function') {
  if (!Symbol.observable) {
    Symbol.observable = Symbol('observable');
  }
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

cartant picture cartant  路  3Comments

Zzzen picture Zzzen  路  3Comments

benlesh picture benlesh  路  3Comments

marcusradell picture marcusradell  路  4Comments

LittleFox94 picture LittleFox94  路  3Comments