X Tutup
Skip to content

Commit 3225770

Browse files
authored
Fix function with properties assigned to variable declaration (#1202)
1 parent 3dc393e commit 3225770

File tree

3 files changed

+43
-9
lines changed

3 files changed

+43
-9
lines changed

src/transformation/visitors/function.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,9 @@ export function transformFunctionLikeDeclaration(
277277

278278
const [functionExpression, functionScope] = transformFunctionToExpression(context, node);
279279

280+
const isNamedFunctionExpression = ts.isFunctionExpression(node) && node.name;
280281
// Handle named function expressions which reference themselves
281-
if (ts.isFunctionExpression(node) && node.name && functionScope.referencedSymbols) {
282+
if (isNamedFunctionExpression && functionScope.referencedSymbols) {
282283
const symbol = context.checker.getSymbolAtLocation(node.name);
283284
if (symbol) {
284285
// TODO: Not using symbol ids because of https://github.com/microsoft/TypeScript/issues/37131
@@ -304,7 +305,9 @@ export function transformFunctionLikeDeclaration(
304305
}
305306
}
306307

307-
return functionExpression;
308+
return isNamedFunctionExpression && isFunctionTypeWithProperties(context.checker.getTypeAtLocation(node))
309+
? createCallableTable(functionExpression)
310+
: functionExpression;
308311
}
309312

310313
export const transformFunctionDeclaration: FunctionVisitor<ts.FunctionDeclaration> = (node, context) => {

src/transformation/visitors/variable-declaration.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -261,20 +261,24 @@ export function transformVariableDeclaration(
261261

262262
// Wrap functions being assigned to a type that contains additional properties in a callable table
263263
// This catches 'const foo = function() {}; foo.bar = "FOOBAR";'
264-
const wrappedValue =
265-
value &&
266-
// Skip named function expressions because they will have been wrapped already
267-
!(statement.initializer && ts.isFunctionExpression(statement.initializer) && statement.initializer.name) &&
268-
isFunctionTypeWithProperties(context.checker.getTypeAtLocation(statement.name))
269-
? createCallableTable(value)
270-
: value;
264+
const wrappedValue = value && shouldWrapInitializerInCallableTable() ? createCallableTable(value) : value;
271265

272266
return createLocalOrExportedOrGlobalDeclaration(context, identifierName, wrappedValue, statement);
273267
} else if (ts.isArrayBindingPattern(statement.name) || ts.isObjectBindingPattern(statement.name)) {
274268
return transformBindingVariableDeclaration(context, statement.name, statement.initializer);
275269
} else {
276270
return assertNever(statement.name);
277271
}
272+
273+
function shouldWrapInitializerInCallableTable() {
274+
assert(statement.initializer);
275+
const initializer = ts.skipOuterExpressions(statement.initializer);
276+
// do not wrap if not a function expression
277+
if (!ts.isFunctionExpression(initializer) && !ts.isArrowFunction(initializer)) return false;
278+
// Skip named function expressions because they will have been wrapped already
279+
if (ts.isFunctionExpression(initializer) && initializer.name) return false;
280+
return isFunctionTypeWithProperties(context.checker.getTypeAtLocation(statement.name));
281+
}
278282
}
279283

280284
export function checkVariableDeclarationList(context: TransformationContext, node: ts.VariableDeclarationList): void {

test/unit/functions/functionProperties.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ test("void function with property assigned to variable", () => {
6060
`.expectToMatchJsResult();
6161
});
6262

63+
test("named function with property assigned to variable", () => {
64+
util.testFunction`
65+
const foo = function baz(s: string) { return s; }
66+
foo.bar = "bar";
67+
return foo("foo") + foo.bar;
68+
`.expectToMatchJsResult();
69+
});
70+
6371
test("recursively referenced function with property assigned to variable", () => {
6472
util.testFunction`
6573
const foo = function(s: string) { return s + foo.bar; };
@@ -173,3 +181,22 @@ test("call function with property using bind method", () => {
173181
return foo.bind("foo", "bar")() + foo.baz;
174182
`.expectToMatchJsResult();
175183
});
184+
185+
// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1196
186+
test("Does not wrap simple variable declaration", () => {
187+
util.testFunction`
188+
function foo(s: string) { return s; }
189+
foo.bar = "bar";
190+
const foo2 = foo;
191+
return foo2("foo") + foo2.bar;
192+
`.expectToMatchJsResult();
193+
});
194+
195+
test("Wraps function in inner expression", () => {
196+
util.testFunction`
197+
type Foo = { (s: string): string; bar: string; }
198+
const foo = (s => s)! as Foo
199+
foo.bar = "bar";
200+
return foo("foo") + foo.bar;
201+
`.expectToMatchJsResult();
202+
});

0 commit comments

Comments
 (0)
X Tutup