Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(operators): support user defined type guards in boolean predicates #56

Merged
merged 1 commit into from
Oct 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions spec/asynciterable-operators/filter-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,26 @@ test('AsyncIterable#filter with index', async t => {
t.end();
});

test('AsyncIterable#filter with typeguard', async t => {
const xs = of<any>(
new String('8'), 5,
new String('7'), 4,
new String('6'), 9,
new String('2'), 1,
new String('0')
);
const ys = filter(xs, (x): x is string => x instanceof String);

const it = ys[Symbol.asyncIterator]();
await hasNext(t, it, new String('8'));
await hasNext(t, it, new String('7'));
await hasNext(t, it, new String('6'));
await hasNext(t, it, new String('2'));
await hasNext(t, it, new String('0'));
await noNext(t, it);
t.end();
});

test('AsyncIterable#filter throws part way through', async t => {
const xs = of(8, 5, 7, 4, 6, 9, 2, 1, 0);
const err = new Error();
Expand Down
20 changes: 20 additions & 0 deletions spec/iterable-operators/filter-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,26 @@ test('Iterable#filter with index', t => {
t.end();
});

test('Iterable#filter with typeguard', t => {
const xs = [
new String('8'), 5,
new String('7'), 4,
new String('6'), 9,
new String('2'), 1,
new String('0')
];
const ys = filter(xs, (x): x is string => x instanceof String);

const it = ys[Symbol.iterator]();
hasNext(t, it, new String('8'));
hasNext(t, it, new String('7'));
hasNext(t, it, new String('6'));
hasNext(t, it, new String('2'));
hasNext(t, it, new String('0'));
noNext(t, it);
t.end();
});

test('Iterable#filter throws part way through', t => {
const xs = [8, 5, 7, 4, 6, 9, 2, 1, 0];
const err = new Error();
Expand Down
2 changes: 1 addition & 1 deletion spec/tape-async.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
declare module "tape-async" {
declare module 'tape-async' {
import * as tape from 'tape';
export = tape;
}
3 changes: 2 additions & 1 deletion src/add/asynciterable-operators/every.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AsyncIterableX } from '../../asynciterable';
import { every } from '../../asynciterable/every';
import { booleanAsyncPredicate } from '../../internal/predicates';

/**
* @ignore
*/
export function everyProto<T>(
this: AsyncIterableX<T>,
comparer: (value: T, index: number) => boolean | Promise<boolean>): Promise<boolean> {
comparer: booleanAsyncPredicate<T>): Promise<boolean> {
return every<T>(this, comparer);
}

Expand Down
3 changes: 2 additions & 1 deletion src/add/asynciterable-operators/filter.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AsyncIterableX } from '../../asynciterable';
import { filter } from '../../asynciterable/filter';
import { booleanAsyncPredicate } from '../../internal/predicates';

/**
* @ignore
*/
export function filterProto<TSource>(
this: AsyncIterable<TSource>,
predicate: (value: TSource, index: number) => Promise<boolean> | boolean,
predicate: booleanAsyncPredicate<TSource>,
thisArg?: any): AsyncIterableX<TSource> {
return filter<TSource>(this, predicate, thisArg);
}
Expand Down
3 changes: 2 additions & 1 deletion src/add/asynciterable-operators/find.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AsyncIterableX } from '../../asynciterable';
import { find } from '../../asynciterable/find';
import { booleanAsyncPredicate } from '../../internal/predicates';

/**
* @ignore
*/
export function findProto<T>(
this: AsyncIterableX<T>,
predicate: (value: T, index: number) => boolean | Promise<boolean>,
predicate: booleanAsyncPredicate<T>,
thisArg?: any): Promise<T | undefined> {
return find(this, predicate, thisArg);
}
Expand Down
3 changes: 2 additions & 1 deletion src/add/asynciterable-operators/first.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AsyncIterableX } from '../../asynciterable';
import { first } from '../../asynciterable/first';
import { booleanAsyncPredicate } from '../../internal/predicates';

/**
* @ignore
*/
export function firstProto<T>(
this: AsyncIterableX<T>,
fn?: (value: T) => boolean | Promise<boolean>): Promise<T | undefined> {
fn?: booleanAsyncPredicate<T>): Promise<T | undefined> {
return first(this, fn);
}

Expand Down
3 changes: 2 additions & 1 deletion src/add/asynciterable-operators/last.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AsyncIterableX } from '../../asynciterable';
import { last } from '../../asynciterable/last';
import { booleanAsyncPredicate } from '../../internal/predicates';

/**
* @ignore
*/
export function lastProto<T>(
this: AsyncIterableX<T>,
selector: (value: T) => boolean | Promise<boolean> = async () => true): Promise<T | undefined> {
selector?: booleanAsyncPredicate<T>): Promise<T | undefined> {
return last(this, selector);
}

Expand Down
3 changes: 2 additions & 1 deletion src/add/asynciterable-operators/partition.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AsyncIterableX } from '../../asynciterable';
import { partition } from '../../asynciterable/partition';
import { booleanAsyncPredicate } from '../../internal/predicates';

/**
* @ignore
*/
export function partitionProto<T>(
this: AsyncIterableX<T>,
fn: (value: T, index: number) => boolean,
fn: booleanAsyncPredicate<T>,
thisArg?: any): AsyncIterableX<T>[] {
return partition<T>(this, fn, thisArg);
}
Expand Down
3 changes: 2 additions & 1 deletion src/add/asynciterable-operators/single.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AsyncIterableX } from '../../asynciterable';
import { single } from '../../asynciterable/single';
import { booleanAsyncPredicate } from '../../internal/predicates';

/**
* @ignore
*/
export function singleProto<T>(
this: AsyncIterableX<T>,
selector: (value: T) => boolean | Promise<boolean> = async () => true): Promise<T | undefined> {
selector?: booleanAsyncPredicate<T>): Promise<T | undefined> {
return single(this, selector);
}

Expand Down
3 changes: 2 additions & 1 deletion src/add/asynciterable-operators/skipwhile.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AsyncIterableX } from '../../asynciterable';
import { skipWhile } from '../../asynciterable/skipwhile';
import { booleanAsyncPredicate } from '../../internal/predicates';

/**
* @ignore
*/
export function skipWhileProto<TSource>(
this: AsyncIterableX<TSource>,
predicate: (value: TSource, index: number) => boolean | Promise<boolean>): AsyncIterableX<TSource> {
predicate: booleanAsyncPredicate<TSource>): AsyncIterableX<TSource> {
return skipWhile(this, predicate);
}

Expand Down
3 changes: 2 additions & 1 deletion src/add/asynciterable-operators/some.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AsyncIterableX } from '../../asynciterable';
import { some } from '../../asynciterable/some';
import { booleanAsyncPredicate } from '../../internal/predicates';

/**
* @ignore
*/
export function someProto<T>(
this: AsyncIterableX<T>,
comparer: (value: T, index: number) => boolean | Promise<boolean>): Promise<boolean> {
comparer: booleanAsyncPredicate<T>): Promise<boolean> {
return some(this, comparer);
}

Expand Down
3 changes: 2 additions & 1 deletion src/add/asynciterable-operators/takewhile.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AsyncIterableX } from '../../asynciterable';
import { takeWhile } from '../../asynciterable/takewhile';
import { booleanAsyncPredicate } from '../../internal/predicates';

/**
* @ignore
*/
export function takeWhileProto<TSource>(
this: AsyncIterableX<TSource>,
predicate: (value: TSource, index: number) => boolean | Promise<boolean>): AsyncIterableX<TSource> {
predicate: booleanAsyncPredicate<TSource>): AsyncIterableX<TSource> {
return takeWhile(this, predicate);
}

Expand Down
4 changes: 3 additions & 1 deletion src/asynciterable/every.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { booleanAsyncPredicate } from '../internal/predicates';

export async function every<T>(
source: AsyncIterable<T>,
comparer: (value: T, index: number) => boolean | Promise<boolean>): Promise<boolean> {
comparer: booleanAsyncPredicate<T>): Promise<boolean> {
let i = 0;
for await (let item of source) {
if (!await comparer(item, i++)) { return false; }
Expand Down
7 changes: 4 additions & 3 deletions src/asynciterable/filter.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { AsyncIterableX } from '../asynciterable';
import { bindCallback } from '../internal/bindcallback';
import { booleanAsyncPredicate } from '../internal/predicates';

class FilterAsyncIterable<TSource> extends AsyncIterableX<TSource> {
private _source: Iterable<TSource | PromiseLike<TSource>> | AsyncIterable<TSource>;
private _predicate: (value: TSource, index: number) => Promise<boolean> | boolean;
private _predicate: booleanAsyncPredicate<TSource>;

constructor(
source: Iterable<TSource | PromiseLike<TSource>> | AsyncIterable<TSource>,
predicate: (value: TSource, index: number) => Promise<boolean> | boolean) {
predicate: booleanAsyncPredicate<TSource>) {
super();
this._source = source;
this._predicate = predicate;
Expand All @@ -25,7 +26,7 @@ class FilterAsyncIterable<TSource> extends AsyncIterableX<TSource> {

export function filter<TSource>(
source: Iterable<TSource | PromiseLike<TSource>> | AsyncIterable<TSource>,
predicate: (value: TSource, index: number) => Promise<boolean> | boolean,
predicate: booleanAsyncPredicate<TSource>,
thisArg?: any): AsyncIterableX<TSource> {
return new FilterAsyncIterable<TSource>(source, bindCallback(predicate, thisArg, 2));
}
3 changes: 2 additions & 1 deletion src/asynciterable/find.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { bindCallback } from '../internal/bindcallback';
import { booleanAsyncPredicate } from '../internal/predicates';

export async function find<T>(
source: AsyncIterable<T>,
predicate: (value: T, index: number) => boolean | Promise<boolean>,
predicate: booleanAsyncPredicate<T>,
thisArg?: any): Promise<T | undefined> {
const fn = bindCallback(predicate, thisArg, 2);
let i = 0;
Expand Down
7 changes: 5 additions & 2 deletions src/asynciterable/first.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { booleanAsyncPredicate } from '../internal/predicates';

export async function first<T>(
source: AsyncIterable<T>,
predicate: (value: T) => boolean | Promise<boolean> = async () => true): Promise<T | undefined> {
predicate: booleanAsyncPredicate<T> = async () => true): Promise<T | undefined> {
let i = 0;
for await (let item of source) {
if (await predicate(item)) {
if (await predicate(item, i++)) {
return item;
}
}
Expand Down
8 changes: 5 additions & 3 deletions src/asynciterable/last.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { booleanAsyncPredicate } from '../internal/predicates';

export async function last<T>(
source: AsyncIterable<T>,
fn: (value: T) => boolean | Promise<boolean> = async () => true): Promise<T | undefined> {
let result: T | undefined;
fn: booleanAsyncPredicate<T> = async () => true): Promise<T | undefined> {
let i = 0, result: T | undefined;
for await (let item of source) {
if (await fn(item)) {
if (await fn(item, i++)) {
result = item;
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/asynciterable/partition.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { AsyncIterableX } from '../asynciterable';
import { filter } from './filter';
import { booleanAsyncPredicate } from '../internal/predicates';

export function partition<TSource>(
source: AsyncIterable<TSource>,
predicate: (value: TSource, index: number) => boolean | Promise<boolean>,
predicate: booleanAsyncPredicate<TSource>,
thisArg?: any): AsyncIterableX<TSource>[] {
return [
filter(source, predicate, thisArg),
Expand Down
9 changes: 6 additions & 3 deletions src/asynciterable/single.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { booleanAsyncPredicate } from '../internal/predicates';

export async function single<T>(
source: AsyncIterable<T>,
selector: (value: T) => boolean | Promise<boolean> = () => true): Promise<T | undefined> {
selector: booleanAsyncPredicate<T> = () => true): Promise<T | undefined> {
let result: T | undefined;
let hasResult = false;
let i = 0;
for await (let item of source) {
if (hasResult && await selector(item)) {
if (hasResult && await selector(item, i++)) {
throw new Error('More than one element was found');
}
if (await selector(item)) {
if (await selector(item, i++)) {
result = item;
hasResult = true;
}
Expand Down
7 changes: 4 additions & 3 deletions src/asynciterable/skipwhile.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AsyncIterableX } from '../asynciterable';
import { booleanAsyncPredicate } from '../internal/predicates';

class SkipWhileAsyncIterable<TSource> extends AsyncIterableX<TSource> {
private _source: AsyncIterable<TSource>;
private _predicate: (value: TSource, index: number) => boolean | Promise<boolean>;
private _predicate: booleanAsyncPredicate<TSource>;

constructor(
source: AsyncIterable<TSource>,
predicate: (value: TSource, index: number) => boolean | Promise<boolean>) {
predicate: booleanAsyncPredicate<TSource>) {
super();
this._source = source;
this._predicate = predicate;
Expand All @@ -23,6 +24,6 @@ class SkipWhileAsyncIterable<TSource> extends AsyncIterableX<TSource> {

export function skipWhile<TSource>(
source: AsyncIterable<TSource>,
predicate: (value: TSource, index: number) => boolean | Promise<boolean>): AsyncIterableX<TSource> {
predicate: booleanAsyncPredicate<TSource>): AsyncIterableX<TSource> {
return new SkipWhileAsyncIterable<TSource>(source, predicate);
}
4 changes: 3 additions & 1 deletion src/asynciterable/some.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { booleanAsyncPredicate } from '../internal/predicates';

export async function some<T>(
source: AsyncIterable<T>,
comparer: (value: T, index: number) => boolean | Promise<boolean>): Promise<boolean> {
comparer: booleanAsyncPredicate<T>): Promise<boolean> {
let i = 0;
for await (let item of source) {
if (await comparer(item, i++)) { return true; }
Expand Down
7 changes: 4 additions & 3 deletions src/asynciterable/takewhile.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AsyncIterableX } from '../asynciterable';
import { booleanAsyncPredicate } from '../internal/predicates';

class TakeWhileAsyncIterable<TSource> extends AsyncIterableX<TSource> {
private _source: AsyncIterable<TSource>;
private _predicate: (value: TSource, index: number) => boolean | Promise<boolean>;
private _predicate: booleanAsyncPredicate<TSource>;

constructor(
source: AsyncIterable<TSource>,
predicate: (value: TSource, index: number) => boolean | Promise<boolean>) {
predicate: booleanAsyncPredicate<TSource>) {
super();
this._source = source;
this._predicate = predicate;
Expand All @@ -23,6 +24,6 @@ class TakeWhileAsyncIterable<TSource> extends AsyncIterableX<TSource> {

export function takeWhile<TSource>(
source: AsyncIterable<TSource>,
predicate: (value: TSource, index: number) => boolean| Promise<boolean>): AsyncIterableX<TSource> {
predicate: booleanAsyncPredicate<TSource>): AsyncIterableX<TSource> {
return new TakeWhileAsyncIterable<TSource>(source, predicate);
}
8 changes: 8 additions & 0 deletions src/internal/predicates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type booleanPredicate<T> =
{ (value: T, index: number): boolean } |
{ (value: T, index: number): value is T };

export type booleanAsyncPredicate<T> =
{ (value: T, index: number): boolean } |
{ (value: T, index: number): value is T } |
{ (value: T, index: number): Promise<boolean> } ;
3 changes: 2 additions & 1 deletion src/iterable/every.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { booleanPredicate } from '../internal/predicates';
/**
* Determines whether every element of a sequence satisfy a condition.
* @param {Iterable<T>} source Source sequence.
Expand All @@ -7,7 +8,7 @@
*/
export function every<T>(
source: Iterable<T>,
comparer: (value: T, index: number) => boolean): boolean {
comparer: booleanPredicate<T>): boolean {
let i = 0;
for (let item of source) {
if (!comparer(item, i++)) { return false; }
Expand Down
Loading