diff --git a/spec/asynciterable/from-spec.ts b/spec/asynciterable/from-spec.ts index fed4c0d7..130c6e8e 100644 --- a/spec/asynciterable/from-spec.ts +++ b/spec/asynciterable/from-spec.ts @@ -126,6 +126,26 @@ test('AsyncIterable#from from promise with selector', async t => { t.end(); }); +test('AsyncIterable#from from non-iterable', async t => { + const xs = {}; + const res = from(xs); + + const it = res[Symbol.asyncIterator](); + await hasNext(t, it, xs); + await noNext(t, it); + t.end(); +}); + +test('AsyncIterable#from from array-like with selector', async t => { + const xs = {}; + const res = from(xs, (x, i) => [x, i]); + + const it = res[Symbol.asyncIterator](); + await hasNext(t, it, [xs, 0]); + await noNext(t, it); + t.end(); +}); + interface Observer { next: (value: T) => void; error: (err: any) => void; diff --git a/spec/iterable/from-spec.ts b/spec/iterable/from-spec.ts index c4fa8aa2..69cf5ae5 100644 --- a/spec/iterable/from-spec.ts +++ b/spec/iterable/from-spec.ts @@ -59,3 +59,23 @@ test('Iterable#from from array-like with selector', t => { noNext(t, it); t.end(); }); + +test('Iterable#from from non-iterable', t => { + const xs = {}; + const res = from(xs); + + const it = res[Symbol.iterator](); + hasNext(t, it, xs); + noNext(t, it); + t.end(); +}); + +test('Iterable#from from non-iterable with selector', t => { + const xs = {}; + const res = from(xs, (x, i) => [x, i]); + + const it = res[Symbol.iterator](); + hasNext(t, it, [xs, 0]); + noNext(t, it); + t.end(); +}); diff --git a/src/asynciterable.ts b/src/asynciterable.ts index 5e46ef6f..494f66de 100644 --- a/src/asynciterable.ts +++ b/src/asynciterable.ts @@ -2,9 +2,8 @@ import { OperatorAsyncFunction } from './interfaces'; import { bindCallback } from './internal/bindcallback'; import { identityAsync } from './internal/identity'; import { toLength } from './internal/tolength'; -import { isIterable, isAsyncIterable } from './internal/isiterable'; +import { isArrayLike, isIterable, isAsyncIterable } from './internal/isiterable'; import { Observable } from './observer'; -import { AsyncIterable } from './Ix'; /** * This clas serves as the base for all operations which support [Symbol.asyncIterator]. @@ -89,14 +88,17 @@ export abstract class AsyncIterableX implements AsyncIterable { } const piped = (input: AsyncIterable): AsyncIterableX => { - return operations.reduce((prev: any, fn: OperatorAsyncFunction) => fn(prev), input); + return operations.reduce( + (prev: any, fn: OperatorAsyncFunction) => fn(prev), + input as any + ); }; return piped(this); } static from( - source: AsyncIterableInput, + source: AsyncIterableInput | TSource, selector: (value: TSource, index: number) => TResult | Promise = identityAsync, thisArg?: any ): AsyncIterableX { @@ -114,8 +116,11 @@ export abstract class AsyncIterableX implements AsyncIterable { if (isArrayLike(source)) { return new FromArrayIterable(source, fn); } + return new FromAsyncIterable( + new OfAsyncIterable([source as TSource]), + fn + ); /* tslint:enable */ - throw new TypeError('Input type not supported'); } static of(...args: TSource[]): AsyncIterableX { @@ -270,10 +275,6 @@ function isObservable(x: any): x is Observable { return x != null && Object(x) === x && typeof x['subscribe'] === 'function'; } -function isArrayLike(x: any): x is ArrayLike { - return x != null && Object(x) === x && typeof x['length'] === 'number'; -} - class OfAsyncIterable extends AsyncIterableX { private _args: TSource[]; diff --git a/src/internal/isiterable.ts b/src/internal/isiterable.ts index db8cbaf7..85a741ef 100644 --- a/src/internal/isiterable.ts +++ b/src/internal/isiterable.ts @@ -1,3 +1,10 @@ +/** + * @ignore + */ +export function isArrayLike(x: any): x is ArrayLike { + return x != null && Object(x) === x && typeof x['length'] === 'number'; +} + /** * @ignore */ diff --git a/src/iterable.ts b/src/iterable.ts index 04eaec50..34ccc19d 100644 --- a/src/iterable.ts +++ b/src/iterable.ts @@ -2,7 +2,7 @@ import { OperatorFunction } from './interfaces'; import { bindCallback } from './internal/bindcallback'; import { identity } from './internal/identity'; import { toLength } from './internal/tolength'; -import { isIterable } from './internal/isiterable'; +import { isArrayLike, isIterable } from './internal/isiterable'; /** * This clas serves as the base for all operations which support [Symbol.iterator]. @@ -85,19 +85,27 @@ export abstract class IterableX implements Iterable { } const piped = (input: Iterable): IterableX => { - return operations.reduce((prev: any, fn: OperatorFunction) => fn(prev), input); + return operations.reduce((prev: any, fn: OperatorFunction) => fn(prev), input as any); }; return piped(this); } static from( - source: Iterable | ArrayLike, - fn: (value: TSource, index: number) => TResult = identity, + source: Iterable | ArrayLike | TSource, + selector: (value: TSource, index: number) => TResult = identity, thisArg?: any ): IterableX { - //tslint:disable-next-line - return new FromIterable(source, bindCallback(fn, thisArg, 2)); + const fn = bindCallback(selector, thisArg, 2); + /* tslint:disable */ + if (isIterable(source)) { + return new FromIterable(source, fn); + } + if (isArrayLike(source)) { + return new FromIterable(source, fn); + } + return new FromIterable(new OfIterable([source as TSource]), fn); + /* tslint:enable */ } static of(...args: TSource[]): IterableX {