Skip to content
This repository was archived by the owner on Jan 26, 2022. It is now read-only.

Commit 06904be

Browse files
Diogo Francomichaelficarra
Diogo Franco
authored andcommitted
Add a polyfill for flatMap and flatten (#21)
* Add a polyfill for flatMap and flatten * Update links to point to tc39 repo * Add new dependencies, .eslintrc * Reformat polyfill, remove outdated comments, use es-abstract * (d.ts) fix compiler errors, add better / working overloads * Correct peer dependency version * Update flattenIntoArray algorithm
1 parent a45500c commit 06904be

File tree

4 files changed

+139
-3
lines changed

4 files changed

+139
-3
lines changed

.eslintrc

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "airbnb-base",
3+
"parserOptions": {
4+
"sourceType": "script"
5+
},
6+
"rules": {
7+
"no-use-before-define": ["error", { "functions": false }]
8+
}
9+
}

package.json

+8-3
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@
22
"name": "proposal-flatmap",
33
"version": "1.0.0",
44
"description": "",
5-
"main": "ecmarkup.js",
5+
"main": "polyfill.js",
66
"dependencies": {
7-
"ecmarkup": "^3.11.5"
7+
"es-abstract": "^1.7.0"
8+
},
9+
"devDependencies": {
10+
"ecmarkup": "^3.11.5",
11+
"eslint": "^3.19.0",
12+
"eslint-config-airbnb-base": "^11.2.0",
13+
"eslint-plugin-import": "^2.7.0"
814
},
9-
"devDependencies": {},
1015
"scripts": {
1116
"test": "echo \"Error: no test specified\" && exit 1",
1217
"build": "ecmarkup proposal.html index.html --verbose --toc",

polyfill.d.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
interface ReadonlyArray<T> {
2+
flatMap<U, This = undefined> (callback: (this: This, value: T, index: number, array: T[]) => U|U[], thisArg?: This): U[]
3+
4+
// adding more overloads would be a combinatorial explosion
5+
flatten<U>(this:
6+
ReadonlyArray<U[][]> |
7+
ReadonlyArray<ReadonlyArray<U[]>> |
8+
ReadonlyArray<ReadonlyArray<U>[]> |
9+
ReadonlyArray<ReadonlyArray<ReadonlyArray<U>>>,
10+
depth: 2): U[];
11+
flatten<U>(this: ReadonlyArray<U[]> | ReadonlyArray<ReadonlyArray<U>>, depth?: 1): U[];
12+
flatten<U>(this: ReadonlyArray<U>, depth: 0): U[];
13+
flatten<U>(depth: number): U[];
14+
}
15+
16+
17+
interface Array<T> {
18+
flatMap<U, This = undefined> (callback: (this: This, value: T, index: number, array: T[]) => U|U[], thisArg?: This): U[]
19+
20+
flatten<U>(this: U[][][][][][][][], depth: 7): U[];
21+
flatten<U>(this: U[][][][][][][], depth: 6): U[];
22+
flatten<U>(this: U[][][][][][], depth: 5): U[];
23+
flatten<U>(this: U[][][][][], depth: 4): U[];
24+
flatten<U>(this: U[][][][], depth: 3): U[];
25+
flatten<U>(this: U[][][], depth: 2): U[];
26+
flatten<U>(this: U[][], depth?: 1): U[];
27+
flatten<U>(this: U[], depth: 0): U[];
28+
flatten<U>(depth?: number): U[];
29+
}

polyfill.js

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
'use strict';
2+
3+
const ES = require('es-abstract');
4+
5+
if (typeof Symbol !== 'function' || !Symbol.isConcatSpreadable) {
6+
throw new Error('Symbol.isConcatSpreadable missing from the environment');
7+
}
8+
9+
if (typeof Array.prototype.flatMap !== 'function') {
10+
// eslint-disable-next-line no-extend-native
11+
Object.defineProperty(Array.prototype, 'flatMap', {
12+
configurable: true,
13+
writable: true,
14+
// https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatMap
15+
value: function flatMap(callback, thisArg = undefined) {
16+
const O = ES.ToObject(this);
17+
// Could ES.IsCallable check callback, but it's better to let it throw a runtime error
18+
const sourceLen = ES.ToLength(O.length);
19+
const A = new (ES.SpeciesConstructor(O, Array))(0);
20+
flattenIntoArray(A, O, sourceLen, 0, 1, callback, thisArg);
21+
return A;
22+
},
23+
});
24+
}
25+
26+
if (typeof Array.prototype.flatten !== 'function') {
27+
// eslint-disable-next-line no-extend-native
28+
Object.defineProperty(Array.prototype, 'flatten', {
29+
configurable: true,
30+
writable: true,
31+
// https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatten
32+
value: function flatten(depthArg = 1) {
33+
const O = ES.ToObject(this);
34+
const sourceLen = ES.ToLength(O.length);
35+
const A = new (ES.SpeciesConstructor(O, Array))(0);
36+
const depth = ES.ToInteger(depthArg);
37+
flattenIntoArray(A, O, sourceLen, 0, depth);
38+
return A;
39+
},
40+
});
41+
}
42+
43+
// https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray
44+
/**
45+
* @param {any[]} target
46+
* @param {any[]} source
47+
* @param {number} startLen
48+
* @param {number} start
49+
* @param {number} depth
50+
* @param {Function=} mapper
51+
* @param {any=} thisArg
52+
*/
53+
function flattenIntoArray(target, source, sourceLen, start, depth, mapper, thisArg) {
54+
let targetIndex = start;
55+
let sourceIndex = 0;
56+
57+
while (sourceIndex < sourceLen) {
58+
const P = ES.ToString(sourceIndex);
59+
if (P in source) {
60+
let element = source[P];
61+
if (mapper) {
62+
element = mapper.call(thisArg, element, sourceIndex, target);
63+
}
64+
let spreadable;
65+
// https://tc39.github.io/ecma262/#sec-isconcatspreadable
66+
if (typeof element !== 'object') {
67+
spreadable = false;
68+
} else {
69+
spreadable = element[Symbol.isConcatSpreadable];
70+
if (typeof spreadable !== 'undefined') {
71+
spreadable = !!spreadable;
72+
} else {
73+
spreadable = Array.isArray(element);
74+
}
75+
}
76+
77+
if (spreadable && depth > 0) {
78+
const elementLen = ES.ToLength(element.length);
79+
const nextIndex = flattenIntoArray(target, element, elementLen, targetIndex, depth - 1);
80+
targetIndex = nextIndex - 1;
81+
} else {
82+
if (targetIndex !== ES.ToLength(targetIndex)) {
83+
throw TypeError();
84+
}
85+
// eslint-disable-next-line no-param-reassign
86+
target[targetIndex] = element;
87+
}
88+
}
89+
targetIndex += 1;
90+
sourceIndex += 1;
91+
}
92+
return targetIndex;
93+
}

0 commit comments

Comments
 (0)