Skip to content

Commit

Permalink
feat(from): make from on non-iterables coerce to iterables
Browse files Browse the repository at this point in the history
  • Loading branch information
trxcllnt committed Oct 27, 2017
1 parent aa84905 commit 9f41842
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 14 deletions.
20 changes: 20 additions & 0 deletions spec/asynciterable/from-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> {
next: (value: T) => void;
error: (err: any) => void;
Expand Down
20 changes: 20 additions & 0 deletions spec/iterable/from-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
19 changes: 10 additions & 9 deletions src/asynciterable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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].
Expand Down Expand Up @@ -89,14 +88,17 @@ export abstract class AsyncIterableX<T> implements AsyncIterable<T> {
}

const piped = (input: AsyncIterable<T>): AsyncIterableX<R> => {
return operations.reduce((prev: any, fn: OperatorAsyncFunction<T, R>) => fn(prev), input);
return operations.reduce(
(prev: any, fn: OperatorAsyncFunction<T, R>) => fn(prev),
input as any
);
};

return piped(this);
}

static from<TSource, TResult = TSource>(
source: AsyncIterableInput<TSource>,
source: AsyncIterableInput<TSource> | TSource,
selector: (value: TSource, index: number) => TResult | Promise<TResult> = identityAsync,
thisArg?: any
): AsyncIterableX<TResult> {
Expand All @@ -115,7 +117,10 @@ export abstract class AsyncIterableX<T> implements AsyncIterable<T> {
return new FromArrayIterable<TSource, TResult>(source, fn);
}
/* tslint:enable */
throw new TypeError('Input type not supported');
return new FromAsyncIterable<TSource, TResult>(
new OfAsyncIterable<TSource>([source as TSource]),
fn
);
}

static of<TSource>(...args: TSource[]): AsyncIterableX<TSource> {
Expand Down Expand Up @@ -270,10 +275,6 @@ function isObservable(x: any): x is Observable<any> {
return x != null && Object(x) === x && typeof x['subscribe'] === 'function';
}

function isArrayLike(x: any): x is ArrayLike<any> {
return x != null && Object(x) === x && typeof x['length'] === 'number';
}

class OfAsyncIterable<TSource> extends AsyncIterableX<TSource> {
private _args: TSource[];

Expand Down
7 changes: 7 additions & 0 deletions src/internal/isiterable.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* @ignore
*/
export function isArrayLike(x: any): x is ArrayLike<any> {
return x != null && Object(x) === x && typeof x['length'] === 'number';
}

/**
* @ignore
*/
Expand Down
17 changes: 12 additions & 5 deletions src/iterable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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].
Expand Down Expand Up @@ -85,19 +85,26 @@ export abstract class IterableX<T> implements Iterable<T> {
}

const piped = (input: Iterable<T>): IterableX<R> => {
return operations.reduce((prev: any, fn: OperatorFunction<T, R>) => fn(prev), input);
return operations.reduce((prev: any, fn: OperatorFunction<T, R>) => fn(prev), input as any);
};

return piped(this);
}

static from<TSource, TResult = TSource>(
source: Iterable<TSource> | ArrayLike<TSource>,
fn: (value: TSource, index: number) => TResult = identity,
source: Iterable<TSource> | ArrayLike<TSource> | TSource,
selector: (value: TSource, index: number) => TResult = identity,
thisArg?: any
): IterableX<TResult> {
const fn = bindCallback(selector, thisArg, 2);
if (isIterable(source)) {
return new FromIterable<TSource, TResult>(source, fn);
}
if (isArrayLike(source)) {
return new FromIterable<TSource, TResult>(source, fn);
}
//tslint:disable-next-line
return new FromIterable<TSource, TResult>(source, bindCallback(fn, thisArg, 2));
return new FromIterable<TSource, TResult>(new OfIterable<TSource>([source as TSource]), fn);
}

static of<TSource>(...args: TSource[]): IterableX<TSource> {
Expand Down

0 comments on commit 9f41842

Please sign in to comment.