From f48622008858123747cf185b4b9da3598589a967 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Sun, 20 Dec 2020 09:36:48 +1000 Subject: [PATCH 1/3] Omit tostring from template literal spans if they are a definite string type --- src/transformation/visitors/template.ts | 8 +++++++- test/unit/templateLiterals.spec.ts | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/transformation/visitors/template.ts b/src/transformation/visitors/template.ts index 784cf7e4e..1b714d765 100644 --- a/src/transformation/visitors/template.ts +++ b/src/transformation/visitors/template.ts @@ -3,6 +3,7 @@ import * as lua from "../../LuaAST"; import { FunctionVisitor } from "../context"; import { ContextType, getDeclarationContextType } from "../utils/function-context"; import { wrapInToStringForConcat } from "../utils/lua-ast"; +import { isStringType } from "../utils/typescript/types"; import { transformArguments, transformContextualCallExpression } from "./call"; // TODO: Source positions @@ -25,7 +26,12 @@ export const transformTemplateExpression: FunctionVisitor for (const span of node.templateSpans) { const expression = context.transformExpression(span.expression); - parts.push(wrapInToStringForConcat(expression)); + const spanType = context.checker.getTypeAtLocation(span.expression); + if (isStringType(context, spanType)) { + parts.push(expression); + } else { + parts.push(wrapInToStringForConcat(expression)); + } const text = span.literal.text; if (text.length > 0) { diff --git a/test/unit/templateLiterals.spec.ts b/test/unit/templateLiterals.spec.ts index 7db192a88..6a9a62bb7 100644 --- a/test/unit/templateLiterals.spec.ts +++ b/test/unit/templateLiterals.spec.ts @@ -60,3 +60,19 @@ test.each(["func`noSelfParameter`", "obj.func`noSelfParameter`", "obj[`func`]`no `.expectToMatchJsResult(); } ); + +test.each([ + ["'any string'", "string"], + ["'string type'", "'string literal type'"], + ["'string intersection'", "string & { x: number }"], +])("tagged template literal omit tostring for string types (%p)", (input, type) => { + util.testFunction` + function func(msg: ${type}) { + return \`func \${msg}\`; + } + + return func(${input} as ${type}); + ` + .tap(builder => expect(builder.getMainLuaCodeChunk()).not.toContain("tostring")) + .expectToMatchJsResult(); +}); From 56ad42d2307c7ab7499d0f0b16513ac45e230dc3 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Sun, 20 Dec 2020 09:56:23 +1000 Subject: [PATCH 2/3] Add negative tests --- test/unit/templateLiterals.spec.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/test/unit/templateLiterals.spec.ts b/test/unit/templateLiterals.spec.ts index 6a9a62bb7..48195fcd4 100644 --- a/test/unit/templateLiterals.spec.ts +++ b/test/unit/templateLiterals.spec.ts @@ -62,17 +62,27 @@ test.each(["func`noSelfParameter`", "obj.func`noSelfParameter`", "obj[`func`]`no ); test.each([ - ["'any string'", "string"], - ["'string type'", "'string literal type'"], - ["'string intersection'", "string & { x: number }"], -])("tagged template literal omit tostring for string types (%p)", (input, type) => { + ["string", false], + ["'string literal type'", false], + ["string & unknown", false], + ["number", true], + ["string | any", true], +])("template span expect tostring for type (%p) - %p", (type, expectToString) => { util.testFunction` function func(msg: ${type}) { - return \`func \${msg}\`; + return \`\${msg}\`; } - return func(${input} as ${type}); + // @ts-ignore + return func(""); ` - .tap(builder => expect(builder.getMainLuaCodeChunk()).not.toContain("tostring")) + .tap(builder => { + const chunk = builder.getMainLuaCodeChunk(); + if (expectToString) { + expect(chunk).toContain("tostring"); + } else { + expect(chunk).not.toContain("tostring"); + } + }) .expectToMatchJsResult(); }); From d833b2ec86cc4c78b7ae4e2614397eacffb0d7a9 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Mon, 21 Dec 2020 09:46:49 +1000 Subject: [PATCH 3/3] Simplify and split templateLiteral tests --- test/unit/templateLiterals.spec.ts | 37 ++++++++++++++---------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/test/unit/templateLiterals.spec.ts b/test/unit/templateLiterals.spec.ts index 48195fcd4..d5dc519a5 100644 --- a/test/unit/templateLiterals.spec.ts +++ b/test/unit/templateLiterals.spec.ts @@ -61,28 +61,25 @@ test.each(["func`noSelfParameter`", "obj.func`noSelfParameter`", "obj[`func`]`no } ); -test.each([ - ["string", false], - ["'string literal type'", false], - ["string & unknown", false], - ["number", true], - ["string | any", true], -])("template span expect tostring for type (%p) - %p", (type, expectToString) => { +test.each(["number", "string | any"])("template span expect tostring for non string type (%p)", type => { util.testFunction` - function func(msg: ${type}) { - return \`\${msg}\`; - } - // @ts-ignore - return func(""); + const msg = "" as ${type}; + return \`\${msg}\`; ` - .tap(builder => { - const chunk = builder.getMainLuaCodeChunk(); - if (expectToString) { - expect(chunk).toContain("tostring"); - } else { - expect(chunk).not.toContain("tostring"); - } - }) + .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain("tostring")) .expectToMatchJsResult(); }); + +test.each(["string", "'string literal type'", "string & unknown"])( + "template span expect no tostring for string-like type (%p)", + type => { + util.testFunction` + // @ts-ignore + const msg = "" as ${type}; + return \`\${msg}\`; + ` + .tap(builder => expect(builder.getMainLuaCodeChunk()).not.toContain("tostring")) + .expectToMatchJsResult(); + } +);