From 96388c35abf45d6107b2783a4536003693393cf3 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Tue, 17 Aug 2021 15:29:23 +0200 Subject: [PATCH 1/2] Throw when a required method is not present --- src/lualib/OptionalMethodCall.ts | 3 +++ src/transformation/visitors/call.ts | 6 ++++-- test/unit/optionalChaining.spec.ts | 11 +++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/lualib/OptionalMethodCall.ts b/src/lualib/OptionalMethodCall.ts index 8829e4a4d..5549a1c36 100644 --- a/src/lualib/OptionalMethodCall.ts +++ b/src/lualib/OptionalMethodCall.ts @@ -2,12 +2,15 @@ function __TS__OptionalMethodCall( this: void, table: Record TReturn>, methodName: string, + isMethodOptional: boolean, ...args: [...TArgs] ): TReturn | undefined { if (table) { const method = table[methodName]; if (method) { return method.call(table, ...args); + } else if (!isMethodOptional) { + throw `${methodName} is not a function`; } } return undefined; diff --git a/src/transformation/visitors/call.ts b/src/transformation/visitors/call.ts index 7add12ddc..b8036021e 100644 --- a/src/transformation/visitors/call.ts +++ b/src/transformation/visitors/call.ts @@ -161,6 +161,7 @@ export function transformContextualCallExpression( node, table, lua.createStringLiteral(left.name.text, left.name), + lua.createBooleanLiteral(node.questionDotToken !== undefined), // Require method is present if no ?.() call ...transformArguments(context, args, signature) ); } else { @@ -331,7 +332,7 @@ function wrapIfRequired( shouldWrapInTable: boolean, shouldWrapOptional: boolean, call: lua.CallExpression | lua.MethodCallExpression, - node: ts.Node + node: ts.CallExpression ): lua.Expression { const wrappedOptional = shouldWrapOptional ? wrapOptionalCall(context, call, node) : call; return shouldWrapInTable ? wrapInTable(wrappedOptional) : wrappedOptional; @@ -340,7 +341,7 @@ function wrapIfRequired( function wrapOptionalCall( context: TransformationContext, call: lua.CallExpression | lua.MethodCallExpression, - node: ts.Node + node: ts.CallExpression ): lua.CallExpression { if (lua.isMethodCallExpression(call)) { return transformLuaLibFunction( @@ -349,6 +350,7 @@ function wrapOptionalCall( node, call.prefixExpression, lua.createStringLiteral(call.name.text), + lua.createBooleanLiteral(node.questionDotToken !== undefined), // Require method is present if no ?.() call ...call.params ); } else { diff --git a/test/unit/optionalChaining.spec.ts b/test/unit/optionalChaining.spec.ts index cf24b6702..69ecf9379 100644 --- a/test/unit/optionalChaining.spec.ts +++ b/test/unit/optionalChaining.spec.ts @@ -144,8 +144,8 @@ describe("optional chaining function calls", () => { const objWithMethods: typeWithOptional = {}; const objWithMethods2: typeWithOptional = { a: { b: { c: () => 4 } } }; - return { - expectNil: objWithMethods.a?.b.c(), + return { + expectNil: objWithMethods.a?.b.c(), expectFour: objWithMethods2.a?.b.c() }; `.expectToMatchJsResult(); @@ -167,4 +167,11 @@ describe("optional chaining function calls", () => { return [obj.a?.().b.c?.() ?? "nil", obj2.a?.().b.c?.() ?? "nil", obj3.a?.().b.c?.() ?? "nil"]; `.expectToMatchJsResult(); }); + + test("incorrect type, method is not a function", () => { + util.testFunction` + const obj: any = {}; + obj?.foo(); + `.expectToEqual(new util.ExecutionError("foo is not a function")); + }); }); From 764f9d16950f95f21c00370dcf314cc5e262fe77 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Fri, 20 Aug 2021 18:49:53 +0200 Subject: [PATCH 2/2] Add issue link to test --- test/unit/optionalChaining.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/optionalChaining.spec.ts b/test/unit/optionalChaining.spec.ts index 69ecf9379..9763a91c6 100644 --- a/test/unit/optionalChaining.spec.ts +++ b/test/unit/optionalChaining.spec.ts @@ -168,6 +168,7 @@ describe("optional chaining function calls", () => { `.expectToMatchJsResult(); }); + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1085 test("incorrect type, method is not a function", () => { util.testFunction` const obj: any = {};