Repository navigation
Observable.from should throw primitive iterables (strings) #125
Description
Activity
I think you misread my comment (which was not all that clear, sorry).
Iterator.fromdoes not throw when given a string. This makes it different fromflatMap, which does throw when the mapper returns a string, even though strings are iterable.See discussion in tc39/proposal-iterator-helpers#244.
Reacted by Jordan Harband, Ashley Claymore and Ben LeshFYI:
ReadableStream.fromrejectsstringwhatwg/webidl#1397If someone is intentionally passing a string into an
X.frommethod, they explicitly want to convert a string into an X. I think this is the one case where an iterable string must be accepted.Reacted by Ben LeshCan a usecase be returning a string from the catch operator?
How should I do this if Observable.from throws on a string? Or can I just return the string?
(having rxjs of("...") in mind)I'm sold on ensuring
Observable.from("string")works the same asObservable.from(Object("string"))does today, by basically adding special handling like https://github.com/tc39/proposal-iterator-helpers/pull/250/files did for Iterator helpers. I'll ensure that this makes it into #160, and I'll close this issue.I'm sold on ensuring
Observable.from("string")works the same asObservable.from(Object("string"))does today, by basically adding special handling like https://github.com/tc39/proposal-iterator-helpers/pull/250/files did for Iterator helpers.I think the behavior of
Observable.from(“string”)andObservable.from(Object(“string”))should be clearly separated.Any time an iterable or async-iterable value (a value that has a
Symbol.iteratororSymbol.asyncIteratormethod) is expected, primitives should be treated as if they were not iterable. Usually, this will mean throwing aTypeError. If the user provides a primitive wrapper Object such as a String Object, however, it should be treated like any other Object.NB: This convention is new as of 2024, and most earlier parts of the language do not follow it. In particular, positional destructuring (both binding and assignment), array spread, argument spread, for-of loops,
yield *, theSetandAggregateErrorconstructors,Object.groupBy,Map.groupBy,Promise.all,Promise.allSettled,Promise.any,Promise.race,Array.from, the static from methods on typed array constructors, andIterator.from(Stage 3 at time of writing) all accept primitives where iterables are expected.If you do so, please specify in the specification that primitive
strings are accepted in exceptional cases.@petamoriken The normative-conventions wording shouldn't apply to explicit coercion methods like
Iterator.fromandObservable.from. (@michaelficarra, who wrote that wording, was aware of this at one point, but he keeps forgetting.)Reacted by Jordan HarbandReacted by Kenta MoriuchiWell, I don't understand why
ReadableStream.fromdoesn't accept primitivestrings whileObservable.fromdoes. Does that meanObservable.fromshould be treated as primitive asIterator.from?If I understand correctly the current plan is for
ReadableStream.fromto accept primitive strings. cc @lucacasonatoIt does not seem so.
FYI: denoland/deno#26882, denoland/deno#25116@bakkot No, the plan is to accept boxed strings, but not primitive strings
Ah. Well, that's unfortunate. In that case I have no opinion about what
Observable.fromshould do, since it can't be consistent with both.The entire point of a "from" method is to explicitly convert from one type to another. It's the one place rejecting strings makes no sense (or rephrased, the one place accepting iterable strings makes sense), and if Readable.from wants to be the weird one, that doesn't mean Observable.from should join it.
Reacted by Dominic Farolino, Kenta Moriuchi and Ben LeshActually what I said may be wrong. I have to read thru the discussion again. Specifically the
async iterablewebidl type will not directly accept string primitives. However we might have decided something else forReadableStream.from. I will have to check again.Reacted by Jordan Harband and Kenta Moriuchi@bakkot and @ljharb thank you for the insight. This makes total sense to me. I'm just used to seeing operators like
flatMapimplement in terms ofObservable.from(fn(value, index++))or whatever, and in those cases, in RxJS at least, it's almost always a bug when someone returns a string from aswitchMap,flatMap/concatMap,mergeMapetc.Reacted by Jordan Harband
Along with how Iterator helpers behave, if a user passes a
string, even though it's an iterable, it's likely an error. We should throw aTypeErrorin that case.