Skip to content

Commit

Permalink
throw when (Async)Iterator#flatMap's mapper returns a non-iterable
Browse files Browse the repository at this point in the history
  • Loading branch information
zloirock committed Nov 7, 2019
1 parent adee014 commit ddf0c8e
Show file tree
Hide file tree
Showing 8 changed files with 31 additions and 21 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
@@ -1,4 +1,7 @@
## Changelog
##### Unreleased
- Throw when `(Async)Iterator#flatMap` mapper returns a non-iterable, per [tc39/proposal-iterator-helpers/55](https://github.com/tc39/proposal-iterator-helpers/issue/55) and [tc39/proposal-iterator-helpers/59](https://github.com/tc39/proposal-iterator-helpers/pull/59)

##### 3.4.0 - 2019.11.07
- Added [well-formed `JSON.stringify`](https://github.com/tc39/proposal-well-formed-stringify), ES2019 feature, thanks [@ExE-Boss](https://github.com/ExE-Boss) and [@WebReflection](https://github.com/WebReflection) for the idea
- Fixed `Math.signbit`, [#687](https://github.com/zloirock/core-js/issues/687), thanks [@chicoxyzzy](https://github.com/chicoxyzzy)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -1807,7 +1807,7 @@ class Iterator {
every(callbackfn: value: any => boolean): boolean;
filter(callbackfn: value: any => boolean): Iterator<any>;
find(callbackfn: value: any => boolean)): any;
flatMap(callbackfn: value => any): Iterator<any>;
flatMap(callbackfn: value => any: Iterable): Iterator<any>;
forEach(callbackfn: value => void): void;
map(callbackfn: value => any): Iterator<any>;
reduce(callbackfn: (memo: any, value: any) => any, initialValue: any): any;
Expand All @@ -1824,7 +1824,7 @@ class AsyncIterator {
every(async callbackfn: value: any => boolean): boolean;
filter(async callbackfn: value: any => boolean): AsyncIterator<any>;
find(async callbackfn: value: any => boolean)): any;
flatMap(async callbackfn: value => any): AsyncIterator<any>;
flatMap(async callbackfn: value => any: Iterable): AsyncIterator<any>;
forEach(async callbackfn: value => void): Promise<void>;
map(async callbackfn: value => any): AsyncIterator<any>;
reduce(async callbackfn: (memo: any, value: any) => any, initialValue: any): Promise<any>;
Expand Down
8 changes: 4 additions & 4 deletions packages/core-js/modules/esnext.async-iterator.flat-map.js
Expand Up @@ -3,7 +3,6 @@
var $ = require('../internals/export');
var aFunction = require('../internals/a-function');
var anObject = require('../internals/an-object');
var isObject = require('../internals/is-object');
var createAsyncIteratorProxy = require('../internals/create-async-iterator-proxy');
var getAsyncIteratorMethod = require('../internals/get-async-iterator-method');

Expand All @@ -23,11 +22,12 @@ var AsyncIteratorProxy = createAsyncIteratorProxy(function (arg, Promise) {
} else {
Promise.resolve(mapper(step.value)).then(function (mapped) {
try {
if (isObject(mapped) && (iteratorMethod = getAsyncIteratorMethod(mapped)) !== undefined) {
state.innerIterator = innerIterator = iteratorMethod.call(mapped);
iteratorMethod = getAsyncIteratorMethod(mapped);
if (iteratorMethod !== undefined) {
state.innerIterator = innerIterator = anObject(iteratorMethod.call(mapped));
state.innerNext = aFunction(innerIterator.next);
return innerLoop();
} resolve({ done: false, value: mapped });
} reject(TypeError('.flatMap callback should return an iterable object'));
} catch (error2) { reject(error2); }
}, reject);
}
Expand Down
13 changes: 7 additions & 6 deletions packages/core-js/modules/esnext.iterator.flat-map.js
Expand Up @@ -3,7 +3,6 @@
var $ = require('../internals/export');
var aFunction = require('../internals/a-function');
var anObject = require('../internals/an-object');
var isObject = require('../internals/is-object');
var getIteratorMethod = require('../internals/get-iterator-method');
var createIteratorProxy = require('../internals/create-iterator-proxy');
var callWithSafeIterationClosing = require('../internals/call-with-safe-iteration-closing');
Expand All @@ -24,12 +23,14 @@ var IteratorProxy = createIteratorProxy(function (arg) {
if (this.done = !!result.done) return;

mapped = callWithSafeIterationClosing(iterator, this.mapper, result.value);
iteratorMethod = getIteratorMethod(mapped);

if (isObject(mapped) && (iteratorMethod = getIteratorMethod(mapped)) !== undefined) {
this.innerIterator = innerIterator = iteratorMethod.call(mapped);
this.innerNext = aFunction(innerIterator.next);
continue;
} return mapped;
if (iteratorMethod === undefined) {
throw TypeError('.flatMap callback should return an iterable object');
}

this.innerIterator = innerIterator = anObject(iteratorMethod.call(mapped));
this.innerNext = aFunction(innerIterator.next);
}
});

Expand Down
5 changes: 3 additions & 2 deletions tests/pure/esnext.async-iterator.flat-map.js
Expand Up @@ -12,12 +12,13 @@ QUnit.test('AsyncIterator#flatMap', assert => {
assert.arity(flatMap, 1);
assert.nonEnumerable(AsyncIterator.prototype, 'flatMap');

flatMap.call(createIterator([1, [], 2, createIterable([3, 4]), [5, 6], 'ab']), it => typeof it == 'number' ? -it : it).toArray().then(it => {
assert.arrayEqual(it, [-1, -2, 3, 4, 5, 6, 'ab'], 'basic functionality');
flatMap.call(createIterator([1, [], 2, createIterable([3, 4]), [5, 6], 'ab']), it => typeof it == 'number' ? [-it] : it).toArray().then(it => {
assert.arrayEqual(it, [-1, -2, 3, 4, 5, 6, 'a', 'b'], 'basic functionality');
return flatMap.call(createIterator([1]), function (arg) {
assert.same(this, STRICT_THIS, 'this');
assert.same(arguments.length, 1, 'arguments length');
assert.same(arg, 1, 'argument');
return [arg];
}).toArray();
}).then(() => {
return flatMap.call(createIterator([1]), () => { throw 42; }).toArray();
Expand Down
6 changes: 4 additions & 2 deletions tests/pure/esnext.iterator.flat-map.js
Expand Up @@ -11,16 +11,18 @@ QUnit.test('Iterator#flatMap', assert => {
assert.nonEnumerable(Iterator.prototype, 'flatMap');

assert.arrayEqual(
flatMap.call(createIterator([1, [], 2, createIterable([3, 4]), [5, 6], 'ab']), it => typeof it == 'number' ? -it : it).toArray(),
[-1, -2, 3, 4, 5, 6, 'ab'],
flatMap.call(createIterator([1, [], 2, createIterable([3, 4]), [5, 6], 'ab']), it => typeof it == 'number' ? [-it] : it).toArray(),
[-1, -2, 3, 4, 5, 6, 'a', 'b'],
'basic functionality',
);
flatMap.call(createIterator([1]), function (arg) {
assert.same(this, STRICT_THIS, 'this');
assert.same(arguments.length, 1, 'arguments length');
assert.same(arg, 1, 'argument');
return [arg];
}).toArray();

assert.throws(() => flatMap.call(createIterator([1]), it => it).next(), TypeError);
assert.throws(() => flatMap.call(undefined, () => { /* empty */ }), TypeError);
assert.throws(() => flatMap.call(null, () => { /* empty */ }), TypeError);
assert.throws(() => flatMap.call({}, () => { /* empty */ }), TypeError);
Expand Down
5 changes: 3 additions & 2 deletions tests/tests/esnext.async-iterator.flat-map.js
Expand Up @@ -12,12 +12,13 @@ QUnit.test('AsyncIterator#flatMap', assert => {
assert.looksNative(flatMap);
assert.nonEnumerable(AsyncIterator.prototype, 'flatMap');

flatMap.call(createIterator([1, [], 2, createIterable([3, 4]), [5, 6], 'ab']), it => typeof it == 'number' ? -it : it).toArray().then(it => {
assert.arrayEqual(it, [-1, -2, 3, 4, 5, 6, 'ab'], 'basic functionality');
flatMap.call(createIterator([1, [], 2, createIterable([3, 4]), [5, 6], 'ab']), it => typeof it == 'number' ? [-it] : it).toArray().then(it => {
assert.arrayEqual(it, [-1, -2, 3, 4, 5, 6, 'a', 'b'], 'basic functionality');
return flatMap.call(createIterator([1]), function (arg) {
assert.same(this, STRICT_THIS, 'this');
assert.same(arguments.length, 1, 'arguments length');
assert.same(arg, 1, 'argument');
return [arg];
}).toArray();
}).then(() => {
return flatMap.call(createIterator([1]), () => { throw 42; }).toArray();
Expand Down
8 changes: 5 additions & 3 deletions tests/tests/esnext.iterator.flat-map.js
Expand Up @@ -11,20 +11,22 @@ QUnit.test('Iterator#flatMap', assert => {
assert.nonEnumerable(Iterator.prototype, 'flatMap');

assert.arrayEqual(
flatMap.call(createIterator([1, [], 2, createIterable([3, 4]), [5, 6], 'ab']), it => typeof it == 'number' ? -it : it).toArray(),
[-1, -2, 3, 4, 5, 6, 'ab'],
flatMap.call(createIterator([1, [], 2, createIterable([3, 4]), [5, 6], 'ab']), it => typeof it == 'number' ? [-it] : it).toArray(),
[-1, -2, 3, 4, 5, 6, 'a', 'b'],
'basic functionality',
);
flatMap.call(createIterator([1]), function (arg) {
assert.same(this, STRICT_THIS, 'this');
assert.same(arguments.length, 1, 'arguments length');
assert.same(arg, 1, 'argument');
return [arg];
}).toArray();

assert.throws(() => flatMap.call([], () => { /* empty */ }), TypeError);
assert.throws(() => flatMap.call(undefined, () => { /* empty */ }), TypeError);
assert.throws(() => flatMap.call(null, () => { /* empty */ }), TypeError);
assert.throws(() => flatMap.call({}, () => { /* empty */ }), TypeError);
assert.throws(() => flatMap.call([], () => { /* empty */ }), TypeError);
assert.throws(() => flatMap.call(createIterator([1]), it => it).next(), TypeError);
assert.throws(() => flatMap.call(createIterator([1]), undefined), TypeError);
assert.throws(() => flatMap.call(createIterator([1]), null), TypeError);
assert.throws(() => flatMap.call(createIterator([1]), {}), TypeError);
Expand Down

0 comments on commit ddf0c8e

Please sign in to comment.