Skip to content

Commit 2c271d7

Browse files
ark120202Perryvw
authored andcommitted
Support rest pattern in destructuring (#568)
* Support rest pattern in destructuring * Remove failing test * Use map for usedProperties * Small destructuring assignment refactor * Tests * Support expression assignments
1 parent c94e070 commit 2c271d7

File tree

7 files changed

+205
-167
lines changed

7 files changed

+205
-167
lines changed

src/LuaAST.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,9 @@ export function createTableIndexExpression(
887887
}
888888

889889
export type AssignmentLeftHandSideExpression = Identifier | TableIndexExpression;
890+
export function isAssignmentLeftHandSideExpression(node: Node): node is AssignmentLeftHandSideExpression {
891+
return isIdentifier(node) || isTableIndexExpression(node);
892+
}
890893

891894
export type FunctionDefinition = (VariableDeclarationStatement | AssignmentStatement) & {
892895
right: [FunctionExpression];

src/LuaLib.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export enum LuaLibFeature {
4141
ObjectEntries = "ObjectEntries",
4242
ObjectFromEntries = "ObjectFromEntries",
4343
ObjectKeys = "ObjectKeys",
44+
ObjectRest = "ObjectRest",
4445
ObjectValues = "ObjectValues",
4546
Set = "Set",
4647
WeakMap = "WeakMap",

src/LuaTransformer.ts

Lines changed: 166 additions & 131 deletions
Large diffs are not rendered by default.

src/TSHelper.ts

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ export function isAssignmentPattern(node: ts.Node): node is ts.AssignmentPattern
6060
return ts.isObjectLiteralExpression(node) || ts.isArrayLiteralExpression(node);
6161
}
6262

63+
export function isDestructuringAssignment(node: ts.Node): node is ts.DestructuringAssignment {
64+
return (
65+
ts.isBinaryExpression(node) &&
66+
node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
67+
isAssignmentPattern(node.left)
68+
);
69+
}
70+
6371
export function getExportable(exportSpecifiers: ts.NamedExports, resolver: EmitResolver): ts.ExportSpecifier[] {
6472
return exportSpecifiers.elements.filter(exportSpecifier => resolver.isValueAliasDeclaration(exportSpecifier));
6573
}
@@ -400,7 +408,7 @@ export function getCustomNodeDirectives(node: ts.Node): Map<DecoratorKind, Decor
400408
const directivesMap = new Map<DecoratorKind, Decorator>();
401409

402410
ts.getJSDocTags(node).forEach(tag => {
403-
const tagName = tag.tagName.escapedText as string;
411+
const tagName = tag.tagName.text;
404412
if (Decorator.isValid(tagName)) {
405413
const dec = new Decorator(tagName, tag.comment ? tag.comment.split(" ") : []);
406414
directivesMap.set(dec.kind, dec);
@@ -908,34 +916,6 @@ export function moduleHasEmittedBody(
908916
return false;
909917
}
910918

911-
export function isValidFlattenableDestructuringAssignmentLeftHandSide(
912-
node: ts.DestructuringAssignment,
913-
checker: ts.TypeChecker,
914-
program: ts.Program
915-
): boolean {
916-
if (ts.isArrayLiteralExpression(node.left)) {
917-
if (node.left.elements.length > 0) {
918-
return !node.left.elements.some(element => {
919-
switch (element.kind) {
920-
case ts.SyntaxKind.Identifier:
921-
case ts.SyntaxKind.PropertyAccessExpression:
922-
if (isArrayLength(element, checker, program)) {
923-
return true;
924-
}
925-
case ts.SyntaxKind.ElementAccessExpression:
926-
// Can be on the left hand side of a Lua assignment statement
927-
return false;
928-
default:
929-
// Cannot be
930-
return true;
931-
}
932-
});
933-
}
934-
}
935-
936-
return false;
937-
}
938-
939919
export function isArrayLength(
940920
expression: ts.Expression,
941921
checker: ts.TypeChecker,
@@ -951,8 +931,10 @@ export function isArrayLength(
951931
}
952932

953933
const name = ts.isPropertyAccessExpression(expression)
954-
? (expression.name.escapedText as string)
955-
: ts.isStringLiteral(expression.argumentExpression) && expression.argumentExpression.text;
934+
? expression.name.text
935+
: ts.isStringLiteral(expression.argumentExpression)
936+
? expression.argumentExpression.text
937+
: undefined;
956938

957939
return name === "length";
958940
}

src/TSTLErrors.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ const getLuaTargetName = (version: LuaTarget) => (version === LuaTarget.LuaJIT ?
77
export const CouldNotCast = (castName: string) =>
88
new Error(`Failed to cast all elements to expected type using ${castName}.`);
99

10-
export const ForbiddenEllipsisDestruction = (node: ts.Node) =>
11-
new TranspileError(`Ellipsis destruction is not allowed.`, node);
12-
1310
export const ForbiddenForIn = (node: ts.Node) =>
1411
new TranspileError(`Iterating over arrays with 'for ... in' is not allowed.`, node);
1512

src/lualib/ObjectRest.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
function __TS__ObjectRest<K extends keyof any, V>(
2+
this: void,
3+
target: Record<K, V>,
4+
usedProperties: Partial<Record<K, true>>
5+
): Partial<Record<K, V>> {
6+
const result: Partial<Record<K, V>> = {};
7+
for (const property in target) {
8+
if (!usedProperties[property]) {
9+
result[property] = target[property];
10+
}
11+
}
12+
13+
return result;
14+
}

test/unit/destructuring.spec.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as util from "../util";
22

3-
const allBindings = "x, y, z";
3+
const allBindings = "x, y, z, rest";
44
const testCases = [
55
{ binding: "{ x }", value: { x: true } },
66
{ binding: "{ x, y }", value: { x: false, y: true } },
@@ -9,12 +9,17 @@ const testCases = [
99
{ binding: "{ x, y = true }", value: { x: false, y: false } },
1010
{ binding: "{ x = true }", value: {} },
1111
{ binding: "{ x, y = true }", value: { x: false } },
12+
{ binding: "{ ...rest }", value: {} },
13+
{ binding: "{ x, ...rest }", value: { x: "x" } },
14+
{ binding: "{ x, ...rest }", value: { x: "x", y: "y", z: "z" } },
1215

1316
{ binding: "[]", value: [] },
1417
{ binding: "[x, y]", value: ["x", "y"] },
1518
{ binding: "[x, , y]", value: ["x", "", "y"] },
1619
{ binding: "[x = true]", value: [false] },
1720
{ binding: "[[x, y]]", value: [["x", "y"]] },
21+
{ binding: "[x, ...rest]", value: ["x"] },
22+
{ binding: "[x, ...rest]", value: ["x", "y", "z"] },
1823

1924
{ binding: "{ y: [z = true] }", value: { y: [false] } },
2025
{ binding: "{ x: [x, y] }", value: { x: ["x", "y"] } },
@@ -47,7 +52,8 @@ test.each(testCases)("in variable declaration (%p)", ({ binding, value }) => {
4752
});
4853

4954
// TODO: https://github.com/TypeScriptToLua/TypeScriptToLua/issues/695
50-
test.each(testCases.filter(x => x.binding !== "[x, , y]"))(
55+
// TODO: https://github.com/microsoft/TypeScript/issues/32656
56+
test.each(testCases.filter(x => x.binding !== "[x, , y]" && x.binding !== "{ x, ...rest }"))(
5157
"in exported variable declaration (%p)",
5258
({ binding, value }) => {
5359
util.testModule`

0 commit comments

Comments
 (0)