diff --git a/src/lualib/ArrayReduce.ts b/src/lualib/ArrayReduce.ts index 9122afa68..db80f4e62 100644 --- a/src/lualib/ArrayReduce.ts +++ b/src/lualib/ArrayReduce.ts @@ -3,25 +3,26 @@ function __TS__ArrayReduce( this: void, arr: T[], callbackFn: (accumulator: T, currentValue: T, index: number, array: T[]) => T, - initial?: T + ...initial: Vararg ): T { const len = arr.length; - if (len === 0 && initial === undefined) { - // tslint:disable-next-line: no-string-throw - throw "Reduce of empty array with no initial value"; - } - let k = 0; - let accumulator = initial; - if (initial === undefined) { + let accumulator = undefined; + + // Check if initial value is present in function call + if (select("#", ...initial) !== 0) { + accumulator = select(1, ...initial); + } else if (len > 0) { accumulator = arr[0]; - k++; + k = 1; + } else { + // tslint:disable-next-line: no-string-throw + throw "Reduce of empty array with no initial value"; } - while (k < len) { - accumulator = callbackFn(accumulator, arr[k], k, arr); - k = k + 1; + for (const i of forRange(k, len - 1)) { + accumulator = callbackFn(accumulator, arr[i], i, arr); } return accumulator; diff --git a/src/lualib/declarations/global.d.ts b/src/lualib/declarations/global.d.ts index e27b9ecea..fc04d3377 100644 --- a/src/lualib/declarations/global.d.ts +++ b/src/lualib/declarations/global.d.ts @@ -16,3 +16,6 @@ declare function rawset(table: T, key: K, val: T[K]): void declare function next(table: Record, index?: K): [K, V]; declare function pcall(func: () => any): any; declare function unpack(list: T[], i?: number, j?: number): T[]; + +declare function select(index: number, ...args: T[]): T; +declare function select(index: "#", ...args: T[]): number; diff --git a/src/lualib/declarations/tstl.d.ts b/src/lualib/declarations/tstl.d.ts index 8441e227b..7a7a8bf4f 100644 --- a/src/lualib/declarations/tstl.d.ts +++ b/src/lualib/declarations/tstl.d.ts @@ -1,5 +1,11 @@ /** @noSelfInFile */ +/** @vararg */ +interface Vararg extends Array {} + +/** @forRange */ +declare function forRange(start: number, limit: number, step?: number): number[]; + interface LuaClass { prototype: LuaObject; ____super?: LuaClass; diff --git a/test/unit/builtins/array.spec.ts b/test/unit/builtins/array.spec.ts index edac44090..7f1cf095b 100644 --- a/test/unit/builtins/array.spec.ts +++ b/test/unit/builtins/array.spec.ts @@ -486,6 +486,22 @@ test.each<[[(total: number, currentItem: number, index: number, array: number[]) util.testExpression`[1, 3, 5, 7].reduce(${util.valuesToString(args)})`.expectToMatchJsResult(); }); +test("array.reduce empty undefined initial", () => { + util.testExpression`[].reduce(() => {}, undefined)`.expectToMatchJsResult(); +}); + +test("array.reduce empty no initial", () => { + util.testExpression`[].reduce(() => {})`.expectToMatchJsResult(true); +}); + +test("array.reduce undefined returning callback", () => { + util.testFunction` + const calls: Array<{ a: void, b: string }> = []; + ["a", "b"].reduce((a, b) => { calls.push({ a, b }) }, undefined); + return calls; + `.expectToMatchJsResult(); +}); + const genericChecks = [ "function generic(array: T)", "function generic(array: T)",