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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

- Fixed iteration over generators stopping at first yielded `nil` value

- Fixed `Array.prototype.join` throwing an error when array contains anything other than strings and numbers

- Fixed extending a class not keeping `toString` implementation from a super class

## 0.33.0
Expand Down
1 change: 1 addition & 0 deletions src/LuaLib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export enum LuaLibFeature {
ArrayFindIndex = "ArrayFindIndex",
ArrayIncludes = "ArrayIncludes",
ArrayIndexOf = "ArrayIndexOf",
ArrayJoin = "ArrayJoin",
ArrayMap = "ArrayMap",
ArrayPush = "ArrayPush",
ArrayReduce = "ArrayReduce",
Expand Down
8 changes: 8 additions & 0 deletions src/lualib/ArrayJoin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
function __TS__ArrayJoin(this: unknown[], separator = ",") {
let result = "";
for (const [index, value] of ipairs(this)) {
if (index > 1) result += separator;
result += value.toString();
}
return result;
}
2 changes: 1 addition & 1 deletion src/lualib/Iterator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ function __TS__Iterator<T>(
const iterator = iterable[Symbol.iterator]();
return [__TS__IteratorIteratorStep, iterator];
} else {
return ipairs(iterable as readonly T[]);
return ipairs(iterable as readonly T[]) as any;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is the reasoning behind this as any?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ipairs returns a LuaTupleIterator which is incompatible with __TS__Iterator signature. Generally this should be resolved with 880

}
}
4 changes: 3 additions & 1 deletion src/lualib/declarations/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@ declare function select<T>(index: "#", ...args: T[]): number;
* @luaIterator
* @tupleReturn
*/
declare function ipairs<T>(t: Record<number, T>): LuaTupleIterable<[number, T]>;
type LuaTupleIterator<T extends any[]> = Iterable<T> & { " LuaTupleIterator": never };

declare function ipairs<T>(t: Record<number, T>): LuaTupleIterator<[number, T]>;
31 changes: 19 additions & 12 deletions src/transformation/builtins/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { TransformationContext } from "../context";
import { unsupportedProperty } from "../utils/diagnostics";
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
import { PropertyCallExpression, transformArguments } from "../visitors/call";
import { isStringType, isNumberType } from "../utils/typescript";

export function transformArrayPrototypeCall(
context: TransformationContext,
Expand Down Expand Up @@ -61,19 +62,25 @@ export function transformArrayPrototypeCall(
case "splice":
return transformLuaLibFunction(context, LuaLibFeature.ArraySplice, node, caller, ...params);
case "join":
const defaultSeparatorLiteral = lua.createStringLiteral(",");
const parameters = [
caller,
node.arguments.length === 0
? defaultSeparatorLiteral
: lua.createBinaryExpression(params[0], defaultSeparatorLiteral, lua.SyntaxKind.OrOperator),
];
const callerType = context.checker.getTypeAtLocation(expression.expression);
const elementType = context.checker.getElementTypeOfArrayType(callerType);
if (elementType && (isStringType(context, elementType) || isNumberType(context, elementType))) {
const defaultSeparatorLiteral = lua.createStringLiteral(",");
const parameters = [
caller,
node.arguments.length === 0
? defaultSeparatorLiteral
: lua.createBinaryExpression(params[0], defaultSeparatorLiteral, lua.SyntaxKind.OrOperator),
];

return lua.createCallExpression(
lua.createTableIndexExpression(lua.createIdentifier("table"), lua.createStringLiteral("concat")),
parameters,
node
);
return lua.createCallExpression(
lua.createTableIndexExpression(lua.createIdentifier("table"), lua.createStringLiteral("concat")),
parameters,
node
);
}

return transformLuaLibFunction(context, LuaLibFeature.ArrayJoin, node, caller, ...params);
case "flat":
return transformLuaLibFunction(context, LuaLibFeature.ArrayFlat, node, caller, ...params);
case "flatMap":
Expand Down
4 changes: 4 additions & 0 deletions src/typescript-internal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,8 @@ declare module "typescript" {
configFile?: TsConfigSourceFile;
configFilePath?: string;
}

interface TypeChecker {
getElementTypeOfArrayType(type: Type): Type | undefined;
}
}
9 changes: 9 additions & 0 deletions test/unit/builtins/array.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,10 +424,19 @@ test.each([
{ array: ["test1", "test2"] },
{ array: ["test1", "test2"], separator: ";" },
{ array: ["test1", "test2"], separator: "" },
{ array: [1, "2"] },
])("array.join (%p)", ({ array, separator }) => {
util.testExpression`${util.formatCode(array)}.join(${util.formatCode(separator)})`.expectToMatchJsResult();
});

test('array.join (1, "2", {})', () => {
util.testExpression`[1, "2", {}].join()`.expectToEqual("1,2,table: 0x168");
});

test('array.join (1, "2", Symbol("foo"))', () => {
util.testExpression`[1, "2", Symbol("foo")].join()`.expectToEqual("1,2,Symbol(foo)");
});

test("array.join without separator argument", () => {
util.testExpression`["test1", "test2"].join()`.expectToMatchJsResult();
});
Expand Down