Skip to content
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
1 change: 1 addition & 0 deletions src/LuaLib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { EmitHost } from "./transpilation";

export enum LuaLibFeature {
ArrayConcat = "ArrayConcat",
ArrayEntries = "ArrayEntries",
ArrayEvery = "ArrayEvery",
ArrayFilter = "ArrayFilter",
ArrayForEach = "ArrayForEach",
Expand Down
14 changes: 14 additions & 0 deletions src/lualib/ArrayEntries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// https://262.ecma-international.org/10.0/#sec-array.prototype.entries
function __TS__ArrayEntries<T>(this: void, array: T[]): IterableIterator<[number, T]> {
let key = 0;
return {
[Symbol.iterator](): IterableIterator<[number, T]> {
return this;
},
next(): IteratorResult<[number, T]> {
const result = { done: array[key] === undefined, value: [key, array[key]] as [number, T] };
key++;
return result;
},
};
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just this?

function __TS__ArrayEntries<T>(this: void, array: T[]) {
    return ipairs(array);
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because TS defines the type of entries as returning IterableIterator<[number, T]>, and I don't think ipairs LuaIterable<LuaMultiReturn<[number, NonNullable<T>]>> is 1:1 assignable to that since it's a different iterator kind and also the lack of LuaMultiReturn.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah, that would be a problem if used indirectly. It's a shame since this version is so much more complex.

It might be good to add a test for indirect use:

...
const entries = array.entries();
for (const [i, v] of entries) {
...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, added a test for indirect use as well as destructuring, those both fail if ipairs is used

2 changes: 2 additions & 0 deletions src/transformation/builtins/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export function transformArrayPrototypeCall(
switch (expressionName) {
case "concat":
return transformLuaLibFunction(context, LuaLibFeature.ArrayConcat, node, caller, ...params);
case "entries":
return transformLuaLibFunction(context, LuaLibFeature.ArrayEntries, node, caller);
case "push":
return transformLuaLibFunction(context, LuaLibFeature.ArrayPush, node, caller, ...params);
case "reverse":
Expand Down
29 changes: 29 additions & 0 deletions test/unit/builtins/array.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,35 @@ describe.each(["reduce", "reduceRight"])("array.%s", reduce => {
});
});

test.each([{ array: [] }, { array: ["a", "b", "c"] }, { array: [{ foo: "foo" }, { bar: "bar" }] }])(
"array.entries (%p)",
({ array }) => {
util.testFunction`
const array = ${util.formatCode(array)};
const result = [];
for (const [i, v] of array.entries()) {
result.push([i, v]);
}
return result;
`.expectToMatchJsResult();
}
);

test("array.entries indirect use", () => {
util.testFunction`
const entries = ["a", "b", "c"].entries();
const result = [];
for (const [i, v] of entries) {
result.push([i, v]);
}
return result;
`.expectToMatchJsResult();
});

test("array.entries destructured", () => {
util.testExpression`[...["a", "b", "c"].entries()]`.expectToMatchJsResult();
});

const genericChecks = [
"function generic<T extends number[]>(array: T)",
"function generic<T extends [...number[]]>(array: T)",
Expand Down