Skip to content

Commit b26ec48

Browse files
authored
Fixed out of spec behavior of array.reduce (#731)
* Fixed out of spec behavior of array.reduce * Moved select declarations from ArrayReduce to lualib declaration files * Fixed issue in reduce with undefined returning callback * Changed reduce loop from while to for for performance reasons * Cleaned up code a little from PR feedback * testcase formatting
1 parent c1e77c9 commit b26ec48

File tree

4 files changed

+38
-12
lines changed

4 files changed

+38
-12
lines changed

src/lualib/ArrayReduce.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,26 @@ function __TS__ArrayReduce<T>(
33
this: void,
44
arr: T[],
55
callbackFn: (accumulator: T, currentValue: T, index: number, array: T[]) => T,
6-
initial?: T
6+
...initial: Vararg<T>
77
): T {
88
const len = arr.length;
99

10-
if (len === 0 && initial === undefined) {
11-
// tslint:disable-next-line: no-string-throw
12-
throw "Reduce of empty array with no initial value";
13-
}
14-
1510
let k = 0;
16-
let accumulator = initial;
17-
if (initial === undefined) {
11+
let accumulator = undefined;
12+
13+
// Check if initial value is present in function call
14+
if (select("#", ...initial) !== 0) {
15+
accumulator = select(1, ...initial);
16+
} else if (len > 0) {
1817
accumulator = arr[0];
19-
k++;
18+
k = 1;
19+
} else {
20+
// tslint:disable-next-line: no-string-throw
21+
throw "Reduce of empty array with no initial value";
2022
}
2123

22-
while (k < len) {
23-
accumulator = callbackFn(accumulator, arr[k], k, arr);
24-
k = k + 1;
24+
for (const i of forRange(k, len - 1)) {
25+
accumulator = callbackFn(accumulator, arr[i], i, arr);
2526
}
2627

2728
return accumulator;

src/lualib/declarations/global.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ declare function rawset<T, K extends keyof T>(table: T, key: K, val: T[K]): void
1616
declare function next<K, V>(table: Record<any, V>, index?: K): [K, V];
1717
declare function pcall(func: () => any): any;
1818
declare function unpack<T>(list: T[], i?: number, j?: number): T[];
19+
20+
declare function select<T>(index: number, ...args: T[]): T;
21+
declare function select<T>(index: "#", ...args: T[]): number;

src/lualib/declarations/tstl.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
/** @noSelfInFile */
22

3+
/** @vararg */
4+
interface Vararg<T> extends Array<T> {}
5+
6+
/** @forRange */
7+
declare function forRange(start: number, limit: number, step?: number): number[];
8+
39
interface LuaClass {
410
prototype: LuaObject;
511
____super?: LuaClass;

test/unit/builtins/array.spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,22 @@ test.each<[[(total: number, currentItem: number, index: number, array: number[])
486486
util.testExpression`[1, 3, 5, 7].reduce(${util.valuesToString(args)})`.expectToMatchJsResult();
487487
});
488488

489+
test("array.reduce empty undefined initial", () => {
490+
util.testExpression`[].reduce(() => {}, undefined)`.expectToMatchJsResult();
491+
});
492+
493+
test("array.reduce empty no initial", () => {
494+
util.testExpression`[].reduce(() => {})`.expectToMatchJsResult(true);
495+
});
496+
497+
test("array.reduce undefined returning callback", () => {
498+
util.testFunction`
499+
const calls: Array<{ a: void, b: string }> = [];
500+
["a", "b"].reduce<void>((a, b) => { calls.push({ a, b }) }, undefined);
501+
return calls;
502+
`.expectToMatchJsResult();
503+
});
504+
489505
const genericChecks = [
490506
"function generic<T extends number[]>(array: T)",
491507
"function generic<T extends [...number[]]>(array: T)",

0 commit comments

Comments
 (0)