Skip to content

Commit 3d1720f

Browse files
authored
Throw when a required method in optional chain is not present (#1091)
* Throw when a required method is not present * Add issue link to test
1 parent 6d9d6b1 commit 3d1720f

File tree

3 files changed

+17
-4
lines changed

3 files changed

+17
-4
lines changed

src/lualib/OptionalMethodCall.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ function __TS__OptionalMethodCall<TArgs extends any[], TReturn>(
22
this: void,
33
table: Record<string, (...args: [...TArgs]) => TReturn>,
44
methodName: string,
5+
isMethodOptional: boolean,
56
...args: [...TArgs]
67
): TReturn | undefined {
78
if (table) {
89
const method = table[methodName];
910
if (method) {
1011
return method.call(table, ...args);
12+
} else if (!isMethodOptional) {
13+
throw `${methodName} is not a function`;
1114
}
1215
}
1316
return undefined;

src/transformation/visitors/call.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ export function transformContextualCallExpression(
161161
node,
162162
table,
163163
lua.createStringLiteral(left.name.text, left.name),
164+
lua.createBooleanLiteral(node.questionDotToken !== undefined), // Require method is present if no ?.() call
164165
...transformArguments(context, args, signature)
165166
);
166167
} else {
@@ -331,7 +332,7 @@ function wrapIfRequired(
331332
shouldWrapInTable: boolean,
332333
shouldWrapOptional: boolean,
333334
call: lua.CallExpression | lua.MethodCallExpression,
334-
node: ts.Node
335+
node: ts.CallExpression
335336
): lua.Expression {
336337
const wrappedOptional = shouldWrapOptional ? wrapOptionalCall(context, call, node) : call;
337338
return shouldWrapInTable ? wrapInTable(wrappedOptional) : wrappedOptional;
@@ -340,7 +341,7 @@ function wrapIfRequired(
340341
function wrapOptionalCall(
341342
context: TransformationContext,
342343
call: lua.CallExpression | lua.MethodCallExpression,
343-
node: ts.Node
344+
node: ts.CallExpression
344345
): lua.CallExpression {
345346
if (lua.isMethodCallExpression(call)) {
346347
return transformLuaLibFunction(
@@ -349,6 +350,7 @@ function wrapOptionalCall(
349350
node,
350351
call.prefixExpression,
351352
lua.createStringLiteral(call.name.text),
353+
lua.createBooleanLiteral(node.questionDotToken !== undefined), // Require method is present if no ?.() call
352354
...call.params
353355
);
354356
} else {

test/unit/optionalChaining.spec.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ describe("optional chaining function calls", () => {
144144
const objWithMethods: typeWithOptional = {};
145145
const objWithMethods2: typeWithOptional = { a: { b: { c: () => 4 } } };
146146
147-
return {
148-
expectNil: objWithMethods.a?.b.c(),
147+
return {
148+
expectNil: objWithMethods.a?.b.c(),
149149
expectFour: objWithMethods2.a?.b.c()
150150
};
151151
`.expectToMatchJsResult();
@@ -167,4 +167,12 @@ describe("optional chaining function calls", () => {
167167
return [obj.a?.().b.c?.() ?? "nil", obj2.a?.().b.c?.() ?? "nil", obj3.a?.().b.c?.() ?? "nil"];
168168
`.expectToMatchJsResult();
169169
});
170+
171+
// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1085
172+
test("incorrect type, method is not a function", () => {
173+
util.testFunction`
174+
const obj: any = {};
175+
obj?.foo();
176+
`.expectToEqual(new util.ExecutionError("foo is not a function"));
177+
});
170178
});

0 commit comments

Comments
 (0)