From 04d7deabc9af706d53cf4894abf54bc3db33578a Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Mon, 18 Jul 2022 17:26:29 -0700 Subject: [PATCH 1/3] Fix function prototype calls for explicitly given function types --- src/transformation/builtins/function.ts | 2 +- src/transformation/builtins/index.ts | 2 ++ test/unit/functions/functions.spec.ts | 23 +++++++++++++++-------- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/transformation/builtins/function.ts b/src/transformation/builtins/function.ts index ebf0a83ca..f2ee043c9 100644 --- a/src/transformation/builtins/function.ts +++ b/src/transformation/builtins/function.ts @@ -4,9 +4,9 @@ import * as lua from "../../LuaAST"; import { TransformationContext } from "../context"; import { unsupportedForTarget, unsupportedProperty, unsupportedSelfFunctionConversion } from "../utils/diagnostics"; import { ContextType, getFunctionContextType } from "../utils/function-context"; -import { createUnpackCall } from "../utils/lua-ast"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; import { transformCallAndArguments } from "../visitors/call"; +import { createUnpackCall } from "../utils/lua-ast"; export function transformFunctionPrototypeCall( context: TransformationContext, diff --git a/src/transformation/builtins/index.ts b/src/transformation/builtins/index.ts index f7ed149b9..b5a92095b 100644 --- a/src/transformation/builtins/index.ts +++ b/src/transformation/builtins/index.ts @@ -145,6 +145,8 @@ function tryTransformBuiltinPropertyCall( case "ReadonlyArray": return transformArrayPrototypeCall(context, node, calledMethod); case "Function": + case "CallableFunction": + case "NewableFunction": return transformFunctionPrototypeCall(context, node, calledMethod); } } diff --git a/test/unit/functions/functions.spec.ts b/test/unit/functions/functions.spec.ts index 6c8819f70..8f2dc1199 100644 --- a/test/unit/functions/functions.spec.ts +++ b/test/unit/functions/functions.spec.ts @@ -159,24 +159,31 @@ test("Class static dot method with parameter", () => { `.expectToMatchJsResult(); }); -test("Function bind", () => { +const functionTypeDeclarations = [ + ["arrow", ": (...args: any) => any"], + ["call signature", ": { (...args: any): any; }"], + ["generic", ": Function"], + ["inferred", ""], +]; + +test.each(functionTypeDeclarations)("Function bind (%s)", (_, type) => { util.testFunction` - const abc = function (this: { a: number }, a: string, b: string) { return this.a + a + b; } + const abc${type} = function (this: { a: number }, a: string, b: string) { return this.a + a + b; } return abc.bind({ a: 4 }, "b")("c"); `.expectToMatchJsResult(); }); -test("Function apply", () => { +test.each(functionTypeDeclarations)("Function apply (%s)", (_, type) => { util.testFunction` - const abc = function (this: { a: number }, a: string) { return this.a + a; } + const abc${type} = function (this: { a: number }, a: string) { return this.a + a; } return abc.apply({ a: 4 }, ["b"]); `.expectToMatchJsResult(); }); // Fix #1226: https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1226 -test("function apply without arguments should not lead to exception", () => { +test.each(functionTypeDeclarations)("function apply without arguments should not lead to exception (%s)", (_, type) => { util.testFunction` - const f = function (this: number) { return this + 3; } + const f${type} = function (this: number) { return this + 3; } return f.apply(4); `.expectToMatchJsResult(); }); @@ -193,9 +200,9 @@ test.each(["() => 4", "undefined"])("prototype call on nullable function (%p)", .expectToMatchJsResult(); }); -test("Function call", () => { +test.each(functionTypeDeclarations)("Function call (%s)", (_, type) => { util.testFunction` - const abc = function (this: { a: number }, a: string) { return this.a + a; } + const abc${type} = function (this: { a: number }, a: string) { return this.a + a; } return abc.call({ a: 4 }, "b"); `.expectToMatchJsResult(); }); From a25689cf83b1ff7940acd348e38c78bbc228c8bd Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Mon, 18 Jul 2022 18:13:51 -0700 Subject: [PATCH 2/3] Fix getPropertyValue type flags optimization. --- src/transformation/utils/language-extensions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/transformation/utils/language-extensions.ts b/src/transformation/utils/language-extensions.ts index f5e446195..dfeb2de21 100644 --- a/src/transformation/utils/language-extensions.ts +++ b/src/transformation/utils/language-extensions.ts @@ -63,8 +63,8 @@ export function getExtensionKindForType(context: TransformationContext, type: ts } const excludedTypeFlags: ts.TypeFlags = - ((1 << 18) - 1) & // All flags from Any...Never - ts.TypeFlags.Index & + ((1 << 18) - 1) | // All flags from Any...Never + ts.TypeFlags.Index | ts.TypeFlags.NonPrimitive; function getPropertyValue(context: TransformationContext, type: ts.Type, propertyName: string): string | undefined { From 95fcc653718f111348d81890f3285b0aec3b0138 Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Mon, 18 Jul 2022 19:42:22 -0700 Subject: [PATCH 3/3] Fix language extension calls in optional chaining --- .../utils/language-extensions.ts | 6 +++++- test/unit/language-extensions/table.spec.ts | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/transformation/utils/language-extensions.ts b/src/transformation/utils/language-extensions.ts index dfeb2de21..bb904471f 100644 --- a/src/transformation/utils/language-extensions.ts +++ b/src/transformation/utils/language-extensions.ts @@ -76,7 +76,11 @@ function getPropertyValue(context: TransformationContext, type: ts.Type, propert } export function getExtensionKindForNode(context: TransformationContext, node: ts.Node): ExtensionKind | undefined { - const type = context.checker.getTypeAtLocation(node); + const originalNode = ts.getOriginalNode(node); + let type = context.checker.getTypeAtLocation(originalNode); + if (ts.isOptionalChain(originalNode)) { + type = context.checker.getNonNullableType(type); + } return getExtensionKindForType(context, type); } diff --git a/test/unit/language-extensions/table.spec.ts b/test/unit/language-extensions/table.spec.ts index 90e9b8394..8f21dc8d1 100644 --- a/test/unit/language-extensions/table.spec.ts +++ b/test/unit/language-extensions/table.spec.ts @@ -424,3 +424,22 @@ describe("LuaTable extension interface", () => { .expectToEqual({ foo: 1, bar: 3, baz: 5 }); }); }); + +test.each([ + [undefined, undefined], + ["new LuaSet()", true], +])("call on optional table with strictNullChecks (%s)", (value, expected) => { + util.testFunction` + function getFoo(): LuaSet | undefined { + return ${value} + } + const foo = getFoo() + foo?.add("foo") + return foo?.has("foo") + ` + .setOptions({ + strictNullChecks: true, + }) + .withLanguageExtensions() + .expectToEqual(expected); +});