Skip to content

Commit 7cd6935

Browse files
authored
Fix optimizing spread to table unpack too often (#1459)
* Fix optimizing spread to table unpack too often * Update test snapshot
1 parent 0e5df55 commit 7cd6935

File tree

4 files changed

+39
-4
lines changed

4 files changed

+39
-4
lines changed

src/transformation/utils/typescript/types.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,22 @@ function isExplicitArrayType(context: TransformationContext, type: ts.Type): boo
7070
return false;
7171
}
7272

73+
function isAlwaysExplicitArrayType(context: TransformationContext, type: ts.Type): boolean {
74+
if (context.checker.isArrayType(type) || context.checker.isTupleType(type)) return true;
75+
if (type.symbol) {
76+
const baseConstraint = context.checker.getBaseConstraintOfType(type);
77+
if (baseConstraint && baseConstraint !== type) {
78+
return isAlwaysExplicitArrayType(context, baseConstraint);
79+
}
80+
}
81+
82+
if (type.isUnionOrIntersection()) {
83+
return type.types.every(t => isAlwaysExplicitArrayType(context, t));
84+
}
85+
86+
return false;
87+
}
88+
7389
/**
7490
* Iterate over a type and its bases until the callback returns true.
7591
*/
@@ -95,6 +111,10 @@ export function isArrayType(context: TransformationContext, type: ts.Type): bool
95111
return forTypeOrAnySupertype(context, type, t => isExplicitArrayType(context, t));
96112
}
97113

114+
export function isAlwaysArrayType(context: TransformationContext, type: ts.Type): boolean {
115+
return forTypeOrAnySupertype(context, type, t => isAlwaysExplicitArrayType(context, t));
116+
}
117+
98118
export function isFunctionType(type: ts.Type): boolean {
99119
return type.getCallSignatures().length > 0;
100120
}

src/transformation/visitors/spread.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
isFunctionScopeWithDefinition,
1414
ScopeType,
1515
} from "../utils/scope";
16-
import { isArrayType, findFirstNonOuterParent } from "../utils/typescript";
16+
import { findFirstNonOuterParent, isAlwaysArrayType } from "../utils/typescript";
1717
import { isMultiReturnCall } from "./language-extensions/multi";
1818
import { isGlobalVarargConstant } from "./language-extensions/vararg";
1919

@@ -96,7 +96,8 @@ export const transformSpreadElement: FunctionVisitor<ts.SpreadElement> = (node,
9696
}
9797

9898
const type = context.checker.getTypeAtLocation(node.expression); // not ts-inner expression, in case of casts
99-
if (isArrayType(context, type)) {
99+
if (isAlwaysArrayType(context, type)) {
100+
// All union members must be arrays to be able to shortcut to unpack call
100101
return createUnpackCall(context, innerExpression, node);
101102
}
102103

test/unit/language-extensions/__snapshots__/vararg.spec.ts.snap

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ f(_G, ____)"
2424
exports[`$vararg invalid use ("function f(s: string[]) {} f($vararg);"): diagnostics 1`] = `"main.ts(2,38): error TSTL: $vararg can only be used in a spread element ('...$vararg') in global scope."`;
2525

2626
exports[`$vararg invalid use ("function foo(...args: string[]) {} function bar() { foo(...$vararg); }"): code 1`] = `
27-
"function foo(self, ...)
27+
"local ____lualib = require("lualib_bundle")
28+
local __TS__Spread = ____lualib.__TS__Spread
29+
function foo(self, ...)
2830
end
2931
function bar(self)
3032
foo(
3133
_G,
32-
table.unpack(____)
34+
__TS__Spread(____)
3335
)
3436
end"
3537
`;

test/unit/spread.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,3 +561,15 @@ test("can spread LuaSet (#1384)", () => {
561561
expect(result).toContain("foo");
562562
expect(result).toContain("bar");
563563
});
564+
565+
// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1426
566+
test.each([true, false])("spread a decorator or array union type (#1426)", choice => {
567+
util.testFunction`
568+
function* things() {
569+
yield "a";
570+
yield "b"
571+
}
572+
const c: boolean = ${util.formatCode(choice)};
573+
return [...(c ? things() : ["c", "d"])];
574+
`.expectToMatchJsResult();
575+
});

0 commit comments

Comments
 (0)