package printer_test import ( "testing" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/emittestutil" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/parsetestutil" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/tstransforms" ) func TestEmit(t *testing.T) { t.Parallel() data := []struct { title string input string output string jsx bool }{ {title: "StringLiteral#1", input: `;"test"`, output: ";\n\"test\";"}, {title: "StringLiteral#2", input: `;'test'`, output: ";\n'test';"}, {title: "NumericLiteral#1", input: `0`, output: `0;`}, {title: "NumericLiteral#2", input: `10_000`, output: `10_000;`}, {title: "BigIntLiteral#1", input: `0n`, output: `0n;`}, {title: "BigIntLiteral#2", input: `10_000n`, output: `10000n;`}, // TODO: Preserve numeric literal separators after Strada migration {title: "BooleanLiteral#1", input: `true`, output: `true;`}, {title: "BooleanLiteral#2", input: `false`, output: `false;`}, {title: "NoSubstitutionTemplateLiteral", input: "``", output: "``;"}, {title: "NoSubstitutionTemplateLiteral#2", input: "`\n`", output: "`\n`;"}, {title: "RegularExpressionLiteral#1", input: `/a/`, output: `/a/;`}, {title: "RegularExpressionLiteral#2", input: `/a/g`, output: `/a/g;`}, {title: "NullLiteral", input: `null`, output: `null;`}, {title: "ThisExpression", input: `this`, output: `this;`}, {title: "SuperExpression", input: `super()`, output: `super();`}, {title: "ImportExpression", input: `import()`, output: `import();`}, {title: "PropertyAccess#1", input: `a.b`, output: `a.b;`}, {title: "PropertyAccess#2", input: `a.#b`, output: `a.#b;`}, {title: "PropertyAccess#3", input: `a?.b`, output: `a?.b;`}, {title: "PropertyAccess#4", input: `a?.b.c`, output: `a?.b.c;`}, {title: "PropertyAccess#5", input: `1..b`, output: `1..b;`}, {title: "PropertyAccess#6", input: `1.0.b`, output: `1.0.b;`}, {title: "PropertyAccess#7", input: `0x1.b`, output: `0x1.b;`}, {title: "PropertyAccess#8", input: `0b1.b`, output: `0b1.b;`}, {title: "PropertyAccess#9", input: `0o1.b`, output: `0o1.b;`}, {title: "PropertyAccess#10", input: `10e1.b`, output: `10e1.b;`}, {title: "PropertyAccess#11", input: `10E1.b`, output: `10E1.b;`}, {title: "PropertyAccess#12", input: `a.b?.c`, output: `a.b?.c;`}, {title: "PropertyAccess#13", input: "a\n.b", output: "a\n .b;"}, {title: "PropertyAccess#14", input: "a.\nb", output: "a.\n b;"}, {title: "ElementAccess#1", input: `a[b]`, output: `a[b];`}, {title: "ElementAccess#2", input: `a?.[b]`, output: `a?.[b];`}, {title: "ElementAccess#3", input: `a?.[b].c`, output: `a?.[b].c;`}, {title: "CallExpression#1", input: `a()`, output: `a();`}, {title: "CallExpression#2", input: `a()`, output: `a();`}, {title: "CallExpression#3", input: `a(b)`, output: `a(b);`}, {title: "CallExpression#4", input: `a(b)`, output: `a(b);`}, {title: "CallExpression#5", input: `a(b).c`, output: `a(b).c;`}, {title: "CallExpression#6", input: `a(b).c`, output: `a(b).c;`}, {title: "CallExpression#7", input: `a?.(b)`, output: `a?.(b);`}, {title: "CallExpression#8", input: `a?.(b)`, output: `a?.(b);`}, {title: "CallExpression#9", input: `a?.(b).c`, output: `a?.(b).c;`}, {title: "CallExpression#10", input: `a?.(b).c`, output: `a?.(b).c;`}, {title: "CallExpression#11", input: `a()`, output: `a();`}, // {title: "CallExpression#12", input: `a()`, output: `a();`}, // TODO: preserve trailing comma after Strada migration {title: "CallExpression#13", input: `a?.b()`, output: `a?.b();`}, {title: "NewExpression#1", input: `new a`, output: `new a;`}, {title: "NewExpression#2", input: `new a.b`, output: `new a.b;`}, {title: "NewExpression#3", input: `new a()`, output: `new a();`}, {title: "NewExpression#4", input: `new a.b()`, output: `new a.b();`}, {title: "NewExpression#5", input: `new a()`, output: `new a();`}, {title: "NewExpression#6", input: `new a.b()`, output: `new a.b();`}, {title: "NewExpression#7", input: `new a(b)`, output: `new a(b);`}, {title: "NewExpression#8", input: `new a.b(c)`, output: `new a.b(c);`}, {title: "NewExpression#9", input: `new a(b)`, output: `new a(b);`}, {title: "NewExpression#10", input: `new a.b(c)`, output: `new a.b(c);`}, {title: "NewExpression#11", input: `new a(b).c`, output: `new a(b).c;`}, {title: "NewExpression#12", input: `new a(b).c`, output: `new a(b).c;`}, {title: "TaggedTemplateExpression#1", input: "tag``", output: "tag ``;"}, {title: "TaggedTemplateExpression#2", input: "tag``", output: "tag ``;"}, {title: "TypeAssertionExpression#1", input: `a`, output: `a;`}, {title: "FunctionExpression#1", input: `(function(){})`, output: `(function () { });`}, {title: "FunctionExpression#2", input: `(function f(){})`, output: `(function f() { });`}, {title: "FunctionExpression#3", input: `(function*f(){})`, output: `(function* f() { });`}, {title: "FunctionExpression#4", input: `(async function f(){})`, output: `(async function f() { });`}, {title: "FunctionExpression#5", input: `(async function*f(){})`, output: `(async function* f() { });`}, {title: "FunctionExpression#6", input: `(function(){})`, output: `(function () { });`}, {title: "FunctionExpression#7", input: `(function(a){})`, output: `(function (a) { });`}, {title: "FunctionExpression#8", input: `(function():T{})`, output: `(function (): T { });`}, {title: "ArrowFunction#1", input: `a=>{}`, output: `a => { };`}, {title: "ArrowFunction#2", input: `()=>{}`, output: `() => { };`}, {title: "ArrowFunction#3", input: `(a)=>{}`, output: `(a) => { };`}, {title: "ArrowFunction#4", input: `(a)=>{}`, output: `(a) => { };`}, {title: "ArrowFunction#5", input: `async a=>{}`, output: `async a => { };`}, {title: "ArrowFunction#6", input: `async()=>{}`, output: `async () => { };`}, {title: "ArrowFunction#7", input: `async()=>{}`, output: `async () => { };`}, {title: "ArrowFunction#8", input: `():T=>{}`, output: `(): T => { };`}, {title: "ArrowFunction#9", input: `()=>a`, output: `() => a;`}, {title: "DeleteExpression", input: `delete a`, output: `delete a;`}, {title: "TypeOfExpression", input: `typeof a`, output: `typeof a;`}, {title: "VoidExpression", input: `void a`, output: `void a;`}, {title: "AwaitExpression", input: `await a`, output: `await a;`}, {title: "PrefixUnaryExpression#1", input: `+a`, output: `+a;`}, {title: "PrefixUnaryExpression#2", input: `++a`, output: `++a;`}, {title: "PrefixUnaryExpression#3", input: `+ +a`, output: `+ +a;`}, {title: "PrefixUnaryExpression#4", input: `+ ++a`, output: `+ ++a;`}, {title: "PrefixUnaryExpression#5", input: `-a`, output: `-a;`}, {title: "PrefixUnaryExpression#6", input: `--a`, output: `--a;`}, {title: "PrefixUnaryExpression#7", input: `- -a`, output: `- -a;`}, {title: "PrefixUnaryExpression#8", input: `- --a`, output: `- --a;`}, {title: "PrefixUnaryExpression#9", input: `+-a`, output: `+-a;`}, {title: "PrefixUnaryExpression#10", input: `+--a`, output: `+--a;`}, {title: "PrefixUnaryExpression#11", input: `-+a`, output: `-+a;`}, {title: "PrefixUnaryExpression#12", input: `-++a`, output: `-++a;`}, {title: "PrefixUnaryExpression#13", input: `~a`, output: `~a;`}, {title: "PrefixUnaryExpression#14", input: `!a`, output: `!a;`}, {title: "PostfixUnaryExpression#1", input: `a++`, output: `a++;`}, {title: "PostfixUnaryExpression#2", input: `a--`, output: `a--;`}, {title: "BinaryExpression#1", input: `a,b`, output: `a, b;`}, {title: "BinaryExpression#2", input: `a+b`, output: `a + b;`}, {title: "BinaryExpression#3", input: `a**b`, output: `a ** b;`}, {title: "BinaryExpression#4", input: `a instanceof b`, output: `a instanceof b;`}, {title: "BinaryExpression#5", input: `a in b`, output: `a in b;`}, {title: "BinaryExpression#6", input: "a\n&& b", output: "a\n && b;"}, {title: "BinaryExpression#7", input: "a &&\nb", output: "a &&\n b;"}, {title: "ConditionalExpression#1", input: `a?b:c`, output: `a ? b : c;`}, {title: "ConditionalExpression#2", input: "a\n?b:c", output: "a\n ? b : c;"}, {title: "ConditionalExpression#3", input: "a?\nb:c", output: "a ?\n b : c;"}, {title: "ConditionalExpression#4", input: "a?b\n:c", output: "a ? b\n : c;"}, {title: "ConditionalExpression#5", input: "a?b:\nc", output: "a ? b :\n c;"}, {title: "TemplateExpression#1", input: "`a${b}c`", output: "`a${b}c`;"}, {title: "TemplateExpression#2", input: "`a${b}c${d}e`", output: "`a${b}c${d}e`;"}, {title: "YieldExpression#1", input: `(function*() { yield })`, output: `(function* () { yield; });`}, {title: "YieldExpression#2", input: `(function*() { yield a })`, output: `(function* () { yield a; });`}, {title: "YieldExpression#3", input: `(function*() { yield*a })`, output: `(function* () { yield* a; });`}, {title: "SpreadElement", input: `[...a]`, output: `[...a];`}, {title: "ClassExpression#1", input: `(class {})`, output: "(class {\n});"}, {title: "ClassExpression#2", input: `(class a {})`, output: "(class a {\n});"}, {title: "ClassExpression#3", input: `(class{})`, output: "(class {\n});"}, {title: "ClassExpression#4", input: `(class a{})`, output: "(class a {\n});"}, {title: "ClassExpression#5", input: `(class extends b {})`, output: "(class extends b {\n});"}, {title: "ClassExpression#6", input: `(class a extends b {})`, output: "(class a extends b {\n});"}, {title: "ClassExpression#7", input: `(class implements b {})`, output: "(class implements b {\n});"}, {title: "ClassExpression#8", input: `(class a implements b {})`, output: "(class a implements b {\n});"}, {title: "ClassExpression#9", input: `(class implements b, c {})`, output: "(class implements b, c {\n});"}, {title: "ClassExpression#10", input: `(class a implements b, c {})`, output: "(class a implements b, c {\n});"}, {title: "ClassExpression#11", input: `(class extends b implements c, d {})`, output: "(class extends b implements c, d {\n});"}, {title: "ClassExpression#12", input: `(class a extends b implements c, d {})`, output: "(class a extends b implements c, d {\n});"}, {title: "ClassExpression#13", input: `(@a class {})`, output: "(\n@a\nclass {\n});"}, {title: "OmittedExpression", input: `[,]`, output: `[,];`}, {title: "ExpressionWithTypeArguments", input: `a`, output: `a;`}, {title: "AsExpression", input: `a as T`, output: `a as T;`}, {title: "SatisfiesExpression", input: `a satisfies T`, output: `a satisfies T;`}, {title: "NonNullExpression", input: `a!`, output: `a!;`}, {title: "MetaProperty#1", input: `new.target`, output: `new.target;`}, {title: "MetaProperty#2", input: `import.meta`, output: `import.meta;`}, {title: "ArrayLiteralExpression#1", input: `[]`, output: `[];`}, {title: "ArrayLiteralExpression#2", input: `[a]`, output: `[a];`}, {title: "ArrayLiteralExpression#3", input: `[a,]`, output: `[a,];`}, {title: "ArrayLiteralExpression#4", input: `[,a]`, output: `[, a];`}, {title: "ArrayLiteralExpression#5", input: `[...a]`, output: `[...a];`}, {title: "ObjectLiteralExpression#1", input: `({})`, output: `({});`}, {title: "ObjectLiteralExpression#2", input: `({a,})`, output: `({ a, });`}, {title: "ShorthandPropertyAssignment", input: `({a})`, output: `({ a });`}, {title: "PropertyAssignment", input: `({a:b})`, output: `({ a: b });`}, {title: "SpreadAssignment", input: `({...a})`, output: `({ ...a });`}, {title: "Block", input: `{}`, output: `{ }`}, {title: "VariableStatement#1", input: `var a`, output: `var a;`}, {title: "VariableStatement#2", input: `let a`, output: `let a;`}, {title: "VariableStatement#3", input: `const a = b`, output: `const a = b;`}, {title: "VariableStatement#4", input: `using a = b`, output: `using a = b;`}, {title: "VariableStatement#5", input: `await using a = b`, output: `await using a = b;`}, {title: "EmptyStatement", input: `;`, output: `;`}, {title: "IfStatement#1", input: `if(a);`, output: "if (a)\n ;"}, {title: "IfStatement#2", input: `if(a);else;`, output: "if (a)\n ;\nelse\n ;"}, {title: "IfStatement#3", input: `if(a);else{}`, output: "if (a)\n ;\nelse { }"}, {title: "IfStatement#4", input: `if(a);else if(b);`, output: "if (a)\n ;\nelse if (b)\n ;"}, {title: "IfStatement#5", input: `if(a);else if(b) {}`, output: "if (a)\n ;\nelse if (b) { }"}, {title: "IfStatement#6", input: `if(a) {}`, output: "if (a) { }"}, {title: "IfStatement#7", input: `if(a) {} else;`, output: "if (a) { }\nelse\n ;"}, {title: "IfStatement#8", input: `if(a) {} else {}`, output: "if (a) { }\nelse { }"}, {title: "IfStatement#9", input: `if(a) {} else if(b);`, output: "if (a) { }\nelse if (b)\n ;"}, {title: "IfStatement#10", input: `if(a) {} else if(b){}`, output: "if (a) { }\nelse if (b) { }"}, {title: "DoStatement#1", input: `do;while(a);`, output: "do\n ;\nwhile (a);"}, {title: "DoStatement#2", input: `do {} while(a);`, output: "do { } while (a);"}, {title: "WhileStatement#1", input: `while(a);`, output: "while (a)\n ;"}, {title: "WhileStatement#2", input: `while(a) {}`, output: "while (a) { }"}, {title: "ForStatement#1", input: `for(;;);`, output: "for (;;)\n ;"}, {title: "ForStatement#2", input: `for(a;;);`, output: "for (a;;)\n ;"}, {title: "ForStatement#3", input: `for(var a;;);`, output: "for (var a;;)\n ;"}, {title: "ForStatement#4", input: `for(;a;);`, output: "for (; a;)\n ;"}, {title: "ForStatement#5", input: `for(;;a);`, output: "for (;; a)\n ;"}, {title: "ForStatement#6", input: `for(;;){}`, output: "for (;;) { }"}, {title: "ForInStatement#1", input: `for(a in b);`, output: "for (a in b)\n ;"}, {title: "ForInStatement#2", input: `for(var a in b);`, output: "for (var a in b)\n ;"}, {title: "ForInStatement#3", input: `for(a in b){}`, output: "for (a in b) { }"}, {title: "ForOfStatement#1", input: `for(a of b);`, output: "for (a of b)\n ;"}, {title: "ForOfStatement#2", input: `for(var a of b);`, output: "for (var a of b)\n ;"}, {title: "ForOfStatement#3", input: `for(a of b){}`, output: "for (a of b) { }"}, {title: "ForOfStatement#4", input: `for await(a of b);`, output: "for await (a of b)\n ;"}, {title: "ForOfStatement#5", input: `for await(var a of b);`, output: "for await (var a of b)\n ;"}, {title: "ForOfStatement#6", input: `for await(a of b){}`, output: "for await (a of b) { }"}, {title: "ContinueStatement#1", input: `continue`, output: "continue;"}, {title: "ContinueStatement#2", input: `continue a`, output: "continue a;"}, {title: "BreakStatement#1", input: `break`, output: "break;"}, {title: "BreakStatement#2", input: `break a`, output: "break a;"}, {title: "ReturnStatement#1", input: `return`, output: "return;"}, {title: "ReturnStatement#2", input: `return a`, output: "return a;"}, {title: "WithStatement#1", input: `with(a);`, output: "with (a)\n ;"}, {title: "WithStatement#2", input: `with(a){}`, output: "with (a) { }"}, {title: "SwitchStatement", input: `switch (a) {}`, output: "switch (a) {\n}"}, {title: "CaseClause#1", input: `switch (a) {case b:}`, output: "switch (a) {\n case b:\n}"}, {title: "CaseClause#2", input: `switch (a) {case b:;}`, output: "switch (a) {\n case b: ;\n}"}, {title: "DefaultClause#1", input: `switch (a) {default:}`, output: "switch (a) {\n default:\n}"}, {title: "DefaultClause#2", input: `switch (a) {default:;}`, output: "switch (a) {\n default: ;\n}"}, {title: "LabeledStatement", input: `a:;`, output: "a: ;"}, {title: "ThrowStatement", input: `throw a`, output: "throw a;"}, {title: "TryStatement#1", input: `try {} catch {}`, output: "try { }\ncatch { }"}, {title: "TryStatement#2", input: `try {} finally {}`, output: "try { }\nfinally { }"}, {title: "TryStatement#3", input: `try {} catch {} finally {}`, output: "try { }\ncatch { }\nfinally { }"}, {title: "DebuggerStatement", input: `debugger`, output: "debugger;"}, {title: "FunctionDeclaration#1", input: `export default function(){}`, output: `export default function () { }`}, {title: "FunctionDeclaration#2", input: `function f(){}`, output: `function f() { }`}, {title: "FunctionDeclaration#3", input: `function*f(){}`, output: `function* f() { }`}, {title: "FunctionDeclaration#4", input: `async function f(){}`, output: `async function f() { }`}, {title: "FunctionDeclaration#5", input: `async function*f(){}`, output: `async function* f() { }`}, {title: "FunctionDeclaration#6", input: `function f(){}`, output: `function f() { }`}, {title: "FunctionDeclaration#7", input: `function f(a){}`, output: `function f(a) { }`}, {title: "FunctionDeclaration#8", input: `function f():T{}`, output: `function f(): T { }`}, {title: "FunctionDeclaration#9", input: `function f();`, output: `function f();`}, {title: "ClassDeclaration#1", input: `class a {}`, output: "class a {\n}"}, {title: "ClassDeclaration#2", input: `class a{}`, output: "class a {\n}"}, {title: "ClassDeclaration#3", input: `class a extends b {}`, output: "class a extends b {\n}"}, {title: "ClassDeclaration#4", input: `class a implements b {}`, output: "class a implements b {\n}"}, {title: "ClassDeclaration#5", input: `class a implements b, c {}`, output: "class a implements b, c {\n}"}, {title: "ClassDeclaration#6", input: `class a extends b implements c, d {}`, output: "class a extends b implements c, d {\n}"}, {title: "ClassDeclaration#7", input: `export default class {}`, output: "export default class {\n}"}, {title: "ClassDeclaration#8", input: `export default class{}`, output: "export default class {\n}"}, {title: "ClassDeclaration#9", input: `export default class extends b {}`, output: "export default class extends b {\n}"}, {title: "ClassDeclaration#10", input: `export default class implements b {}`, output: "export default class implements b {\n}"}, {title: "ClassDeclaration#11", input: `export default class implements b, c {}`, output: "export default class implements b, c {\n}"}, {title: "ClassDeclaration#12", input: `export default class extends b implements c, d {}`, output: "export default class extends b implements c, d {\n}"}, {title: "ClassDeclaration#13", input: `@a class b {}`, output: "@a\nclass b {\n}"}, {title: "ClassDeclaration#14", input: `@a export class b {}`, output: "@a\nexport class b {\n}"}, {title: "ClassDeclaration#15", input: `export @a class b {}`, output: "export \n@a\nclass b {\n}"}, {title: "InterfaceDeclaration#1", input: `interface a {}`, output: "interface a {\n}"}, {title: "InterfaceDeclaration#2", input: `interface a{}`, output: "interface a {\n}"}, {title: "InterfaceDeclaration#3", input: `interface a extends b {}`, output: "interface a extends b {\n}"}, {title: "InterfaceDeclaration#4", input: `interface a extends b, c {}`, output: "interface a extends b, c {\n}"}, {title: "TypeAliasDeclaration#1", input: `type a = b`, output: "type a = b;"}, {title: "TypeAliasDeclaration#2", input: `type a = b`, output: "type a = b;"}, {title: "EnumDeclaration#1", input: `enum a{}`, output: "enum a {\n}"}, {title: "EnumDeclaration#2", input: `enum a{b}`, output: "enum a {\n b\n}"}, {title: "EnumDeclaration#3", input: `enum a{b=c}`, output: "enum a {\n b = c\n}"}, {title: "ModuleDeclaration#1", input: `module a{}`, output: "module a { }"}, {title: "ModuleDeclaration#2", input: `module a.b{}`, output: "module a.b { }"}, {title: "ModuleDeclaration#3", input: `module "a";`, output: "module \"a\";"}, {title: "ModuleDeclaration#4", input: `module "a"{}`, output: "module \"a\" { }"}, {title: "ModuleDeclaration#5", input: `namespace a{}`, output: "namespace a { }"}, {title: "ModuleDeclaration#6", input: `namespace a.b{}`, output: "namespace a.b { }"}, {title: "ModuleDeclaration#7", input: `global;`, output: "global;"}, {title: "ModuleDeclaration#8", input: `global{}`, output: "global { }"}, {title: "ImportEqualsDeclaration#1", input: `import a = b`, output: "import a = b;"}, {title: "ImportEqualsDeclaration#2", input: `import a = b.c`, output: "import a = b.c;"}, {title: "ImportEqualsDeclaration#3", input: `import a = require("b")`, output: "import a = require(\"b\");"}, {title: "ImportEqualsDeclaration#4", input: `export import a = b`, output: "export import a = b;"}, {title: "ImportEqualsDeclaration#5", input: `export import a = require("b")`, output: "export import a = require(\"b\");"}, {title: "ImportEqualsDeclaration#6", input: `import type a = b`, output: "import type a = b;"}, {title: "ImportEqualsDeclaration#7", input: `import type a = b.c`, output: "import type a = b.c;"}, {title: "ImportEqualsDeclaration#8", input: `import type a = require("b")`, output: "import type a = require(\"b\");"}, {title: "ImportDeclaration#1", input: `import "a"`, output: "import \"a\";"}, {title: "ImportDeclaration#2", input: `import a from "b"`, output: "import a from \"b\";"}, {title: "ImportDeclaration#3", input: `import type a from "b"`, output: "import type a from \"b\";"}, {title: "ImportDeclaration#4", input: `import * as a from "b"`, output: "import * as a from \"b\";"}, {title: "ImportDeclaration#5", input: `import type * as a from "b"`, output: "import type * as a from \"b\";"}, {title: "ImportDeclaration#6", input: `import {} from "b"`, output: "import {} from \"b\";"}, {title: "ImportDeclaration#7", input: `import type {} from "b"`, output: "import type {} from \"b\";"}, {title: "ImportDeclaration#8", input: `import { a } from "b"`, output: "import { a } from \"b\";"}, {title: "ImportDeclaration#9", input: `import type { a } from "b"`, output: "import type { a } from \"b\";"}, {title: "ImportDeclaration#8", input: `import { a as b } from "c"`, output: "import { a as b } from \"c\";"}, {title: "ImportDeclaration#9", input: `import type { a as b } from "c"`, output: "import type { a as b } from \"c\";"}, {title: "ImportDeclaration#10", input: `import { "a" as b } from "c"`, output: "import { \"a\" as b } from \"c\";"}, {title: "ImportDeclaration#11", input: `import type { "a" as b } from "c"`, output: "import type { \"a\" as b } from \"c\";"}, {title: "ImportDeclaration#12", input: `import a, {} from "b"`, output: "import a, {} from \"b\";"}, {title: "ImportDeclaration#13", input: `import a, * as b from "c"`, output: "import a, * as b from \"c\";"}, {title: "ImportDeclaration#14", input: `import {} from "a" with {}`, output: "import {} from \"a\" with {};"}, {title: "ImportDeclaration#15", input: `import {} from "a" with { b: "c" }`, output: "import {} from \"a\" with { b: \"c\" };"}, {title: "ImportDeclaration#16", input: `import {} from "a" with { "b": "c" }`, output: "import {} from \"a\" with { \"b\": \"c\" };"}, {title: "ExportAssignment#1", input: `export = a`, output: "export = a;"}, {title: "ExportAssignment#2", input: `export default a`, output: "export default a;"}, {title: "NamespaceExportDeclaration", input: `export as namespace a`, output: "export as namespace a;"}, {title: "ExportDeclaration#1", input: `export * from "a"`, output: "export * from \"a\";"}, {title: "ExportDeclaration#2", input: `export type * from "a"`, output: "export type * from \"a\";"}, {title: "ExportDeclaration#3", input: `export * as a from "b"`, output: "export * as a from \"b\";"}, {title: "ExportDeclaration#4", input: `export type * as a from "b"`, output: "export type * as a from \"b\";"}, {title: "ExportDeclaration#5", input: `export { } from "a"`, output: "export {} from \"a\";"}, {title: "ExportDeclaration#6", input: `export type { } from "a"`, output: "export type {} from \"a\";"}, {title: "ExportDeclaration#7", input: `export { a } from "b"`, output: "export { a } from \"b\";"}, {title: "ExportDeclaration#8", input: `export { type a } from "b"`, output: "export { type a } from \"b\";"}, {title: "ExportDeclaration#9", input: `export type { a } from "b"`, output: "export type { a } from \"b\";"}, {title: "ExportDeclaration#10", input: `export { a as b } from "c"`, output: "export { a as b } from \"c\";"}, {title: "ExportDeclaration#11", input: `export { type a as b } from "c"`, output: "export { type a as b } from \"c\";"}, {title: "ExportDeclaration#12", input: `export type { a as b } from "c"`, output: "export type { a as b } from \"c\";"}, {title: "ExportDeclaration#13", input: `export { a as "b" } from "c"`, output: "export { a as \"b\" } from \"c\";"}, {title: "ExportDeclaration#14", input: `export { type a as "b" } from "c"`, output: "export { type a as \"b\" } from \"c\";"}, {title: "ExportDeclaration#15", input: `export type { a as "b" } from "c"`, output: "export type { a as \"b\" } from \"c\";"}, {title: "ExportDeclaration#16", input: `export { "a" } from "b"`, output: "export { \"a\" } from \"b\";"}, {title: "ExportDeclaration#17", input: `export { type "a" } from "b"`, output: "export { type \"a\" } from \"b\";"}, {title: "ExportDeclaration#18", input: `export type { "a" } from "b"`, output: "export type { \"a\" } from \"b\";"}, {title: "ExportDeclaration#19", input: `export { "a" as b } from "c"`, output: "export { \"a\" as b } from \"c\";"}, {title: "ExportDeclaration#20", input: `export { type "a" as b } from "c"`, output: "export { type \"a\" as b } from \"c\";"}, {title: "ExportDeclaration#21", input: `export type { "a" as b } from "c"`, output: "export type { \"a\" as b } from \"c\";"}, {title: "ExportDeclaration#22", input: `export { "a" as "b" } from "c"`, output: "export { \"a\" as \"b\" } from \"c\";"}, {title: "ExportDeclaration#23", input: `export { type "a" as "b" } from "c"`, output: "export { type \"a\" as \"b\" } from \"c\";"}, {title: "ExportDeclaration#24", input: `export type { "a" as "b" } from "c"`, output: "export type { \"a\" as \"b\" } from \"c\";"}, {title: "ExportDeclaration#25", input: `export { }`, output: "export {};"}, {title: "ExportDeclaration#26", input: `export type { }`, output: "export type {};"}, {title: "ExportDeclaration#27", input: `export { a }`, output: "export { a };"}, {title: "ExportDeclaration#28", input: `export { type a }`, output: "export { type a };"}, {title: "ExportDeclaration#29", input: `export type { a }`, output: "export type { a };"}, {title: "ExportDeclaration#30", input: `export { a as b }`, output: "export { a as b };"}, {title: "ExportDeclaration#31", input: `export { type a as b }`, output: "export { type a as b };"}, {title: "ExportDeclaration#32", input: `export type { a as b }`, output: "export type { a as b };"}, {title: "ExportDeclaration#33", input: `export { a as "b" }`, output: "export { a as \"b\" };"}, {title: "ExportDeclaration#34", input: `export { type a as "b" }`, output: "export { type a as \"b\" };"}, {title: "ExportDeclaration#35", input: `export type { a as "b" }`, output: "export type { a as \"b\" };"}, {title: "ExportDeclaration#36", input: `export {} from "a" with {}`, output: "export {} from \"a\" with {};"}, {title: "ExportDeclaration#37", input: `export {} from "a" with { b: "c" }`, output: "export {} from \"a\" with { b: \"c\" };"}, {title: "ExportDeclaration#38", input: `export {} from "a" with { "b": "c" }`, output: "export {} from \"a\" with { \"b\": \"c\" };"}, {title: "KeywordTypeNode#1", input: `type T = any`, output: `type T = any;`}, {title: "KeywordTypeNode#2", input: `type T = unknown`, output: `type T = unknown;`}, {title: "KeywordTypeNode#3", input: `type T = never`, output: `type T = never;`}, {title: "KeywordTypeNode#4", input: `type T = void`, output: `type T = void;`}, {title: "KeywordTypeNode#5", input: `type T = undefined`, output: `type T = undefined;`}, {title: "KeywordTypeNode#6", input: `type T = null`, output: `type T = null;`}, {title: "KeywordTypeNode#7", input: `type T = object`, output: `type T = object;`}, {title: "KeywordTypeNode#8", input: `type T = string`, output: `type T = string;`}, {title: "KeywordTypeNode#9", input: `type T = symbol`, output: `type T = symbol;`}, {title: "KeywordTypeNode#10", input: `type T = number`, output: `type T = number;`}, {title: "KeywordTypeNode#11", input: `type T = bigint`, output: `type T = bigint;`}, {title: "KeywordTypeNode#12", input: `type T = boolean`, output: `type T = boolean;`}, {title: "KeywordTypeNode#13", input: `type T = intrinsic`, output: `type T = intrinsic;`}, {title: "TypePredicateNode#1", input: `function f(): asserts a`, output: `function f(): asserts a;`}, {title: "TypePredicateNode#2", input: `function f(): asserts a is b`, output: `function f(): asserts a is b;`}, {title: "TypePredicateNode#3", input: `function f(): asserts this`, output: `function f(): asserts this;`}, {title: "TypePredicateNode#4", input: `function f(): asserts this is b`, output: `function f(): asserts this is b;`}, {title: "TypeReferenceNode#1", input: `type T = a`, output: `type T = a;`}, {title: "TypeReferenceNode#2", input: `type T = a.b`, output: `type T = a.b;`}, {title: "TypeReferenceNode#3", input: `type T = a`, output: `type T = a;`}, {title: "TypeReferenceNode#4", input: `type T = a.b`, output: `type T = a.b;`}, {title: "FunctionTypeNode#1", input: `type T = () => a`, output: `type T = () => a;`}, {title: "FunctionTypeNode#2", input: `type T = () => a`, output: `type T = () => a;`}, {title: "FunctionTypeNode#3", input: `type T = (a) => b`, output: `type T = (a) => b;`}, {title: "ConstructorTypeNode#1", input: `type T = new () => a`, output: `type T = new () => a;`}, {title: "ConstructorTypeNode#2", input: `type T = new () => a`, output: `type T = new () => a;`}, {title: "ConstructorTypeNode#3", input: `type T = new (a) => b`, output: `type T = new (a) => b;`}, {title: "ConstructorTypeNode#4", input: `type T = abstract new () => a`, output: `type T = abstract new () => a;`}, {title: "TypeQueryNode#1", input: `type T = typeof a`, output: `type T = typeof a;`}, {title: "TypeQueryNode#2", input: `type T = typeof a.b`, output: `type T = typeof a.b;`}, {title: "TypeQueryNode#3", input: `type T = typeof a`, output: `type T = typeof a;`}, {title: "TypeLiteralNode#1", input: `type T = {}`, output: `type T = {};`}, {title: "TypeLiteralNode#2", input: `type T = {a}`, output: "type T = {\n a;\n};"}, {title: "ArrayTypeNode", input: `type T = a[]`, output: "type T = a[];"}, {title: "TupleTypeNode#1", input: `type T = []`, output: "type T = [\n];"}, {title: "TupleTypeNode#2", input: `type T = [a]`, output: "type T = [\n a\n];"}, {title: "TupleTypeNode#3", input: `type T = [a,]`, output: "type T = [\n a\n];"}, {title: "RestTypeNode", input: `type T = [...a]`, output: "type T = [\n ...a\n];"}, {title: "OptionalTypeNode", input: `type T = [a?]`, output: "type T = [\n a?\n];"}, {title: "NamedTupleMember#1", input: `type T = [a: b]`, output: "type T = [\n a: b\n];"}, {title: "NamedTupleMember#2", input: `type T = [a?: b]`, output: "type T = [\n a?: b\n];"}, {title: "NamedTupleMember#3", input: `type T = [...a: b]`, output: "type T = [\n ...a: b\n];"}, {title: "UnionTypeNode#1", input: `type T = a | b`, output: "type T = a | b;"}, {title: "UnionTypeNode#2", input: `type T = a | b | c`, output: "type T = a | b | c;"}, {title: "UnionTypeNode#3", input: `type T = | a | b`, output: "type T = a | b;"}, {title: "IntersectionTypeNode#1", input: `type T = a & b`, output: "type T = a & b;"}, {title: "IntersectionTypeNode#2", input: `type T = a & b & c`, output: "type T = a & b & c;"}, {title: "IntersectionTypeNode#3", input: `type T = & a & b`, output: "type T = a & b;"}, {title: "ConditionalTypeNode", input: `type T = a extends b ? c : d`, output: "type T = a extends b ? c : d;"}, {title: "InferTypeNode#1", input: `type T = a extends infer b ? c : d`, output: "type T = a extends infer b ? c : d;"}, {title: "InferTypeNode#2", input: `type T = a extends infer b extends c ? d : e`, output: "type T = a extends infer b extends c ? d : e;"}, {title: "ParenthesizedTypeNode", input: `type T = (U)`, output: "type T = (U);"}, {title: "ThisTypeNode", input: `type T = this`, output: "type T = this;"}, {title: "TypeOperatorNode#1", input: `type T = keyof U`, output: "type T = keyof U;"}, {title: "TypeOperatorNode#2", input: `type T = readonly U[]`, output: "type T = readonly U[];"}, {title: "TypeOperatorNode#3", input: `type T = unique symbol`, output: "type T = unique symbol;"}, {title: "IndexedAccessTypeNode", input: `type T = a[b]`, output: "type T = a[b];"}, {title: "MappedTypeNode#1", input: `type T = { [a in b]: c }`, output: "type T = {\n [a in b]: c;\n};"}, {title: "MappedTypeNode#2", input: `type T = { [a in b as c]: d }`, output: "type T = {\n [a in b as c]: d;\n};"}, {title: "MappedTypeNode#3", input: `type T = { readonly [a in b]: c }`, output: "type T = {\n readonly [a in b]: c;\n};"}, {title: "MappedTypeNode#4", input: `type T = { +readonly [a in b]: c }`, output: "type T = {\n +readonly [a in b]: c;\n};"}, {title: "MappedTypeNode#5", input: `type T = { -readonly [a in b]: c }`, output: "type T = {\n -readonly [a in b]: c;\n};"}, {title: "MappedTypeNode#6", input: `type T = { [a in b]?: c }`, output: "type T = {\n [a in b]?: c;\n};"}, {title: "MappedTypeNode#7", input: `type T = { [a in b]+?: c }`, output: "type T = {\n [a in b]+?: c;\n};"}, {title: "MappedTypeNode#8", input: `type T = { [a in b]-?: c }`, output: "type T = {\n [a in b]-?: c;\n};"}, {title: "MappedTypeNode#9", input: `type T = { [a in b]: c; d }`, output: "type T = {\n [a in b]: c;\n d;\n};"}, {title: "LiteralTypeNode#1", input: `type T = null`, output: "type T = null;"}, {title: "LiteralTypeNode#2", input: `type T = true`, output: "type T = true;"}, {title: "LiteralTypeNode#3", input: `type T = false`, output: "type T = false;"}, {title: "LiteralTypeNode#4", input: `type T = ""`, output: "type T = \"\";"}, {title: "LiteralTypeNode#5", input: "type T = ''", output: "type T = '';"}, {title: "LiteralTypeNode#6", input: "type T = ``", output: "type T = ``;"}, {title: "LiteralTypeNode#7", input: `type T = 0`, output: "type T = 0;"}, {title: "LiteralTypeNode#8", input: `type T = 0n`, output: "type T = 0n;"}, {title: "LiteralTypeNode#9", input: `type T = -0`, output: "type T = -0;"}, {title: "LiteralTypeNode#10", input: `type T = -0n`, output: "type T = -0n;"}, {title: "TemplateTypeNode#1", input: "type T = `a${b}c`", output: "type T = `a${b}c`;"}, {title: "TemplateTypeNode#2", input: "type T = `a${b}c${d}e`", output: "type T = `a${b}c${d}e`;"}, {title: "ImportTypeNode#1", input: `type T = import(a)`, output: "type T = import(a);"}, {title: "ImportTypeNode#2", input: `type T = import(a).b`, output: "type T = import(a).b;"}, {title: "ImportTypeNode#3", input: `type T = import(a).b`, output: "type T = import(a).b;"}, {title: "ImportTypeNode#4", input: `type T = typeof import(a)`, output: "type T = typeof import(a);"}, {title: "ImportTypeNode#5", input: `type T = typeof import(a).b`, output: "type T = typeof import(a).b;"}, {title: "ImportTypeNode#6", input: `type T = import(a, { with: { } })`, output: "type T = import(a, { with: {} });"}, {title: "ImportTypeNode#6", input: `type T = import(a, { with: { b: "c" } })`, output: "type T = import(a, { with: { b: \"c\" } });"}, {title: "ImportTypeNode#7", input: `type T = import(a, { with: { "b": "c" } })`, output: "type T = import(a, { with: { \"b\": \"c\" } });"}, {title: "PropertySignature#1", input: "interface I {a}", output: "interface I {\n a;\n}"}, {title: "PropertySignature#2", input: "interface I {readonly a}", output: "interface I {\n readonly a;\n}"}, {title: "PropertySignature#3", input: "interface I {\"a\"}", output: "interface I {\n \"a\";\n}"}, {title: "PropertySignature#4", input: "interface I {'a'}", output: "interface I {\n 'a';\n}"}, {title: "PropertySignature#5", input: "interface I {0}", output: "interface I {\n 0;\n}"}, {title: "PropertySignature#6", input: "interface I {0n}", output: "interface I {\n 0n;\n}"}, {title: "PropertySignature#7", input: "interface I {[a]}", output: "interface I {\n [a];\n}"}, {title: "PropertySignature#8", input: "interface I {a?}", output: "interface I {\n a?;\n}"}, {title: "PropertySignature#9", input: "interface I {a: b}", output: "interface I {\n a: b;\n}"}, {title: "MethodSignature#1", input: "interface I {a()}", output: "interface I {\n a();\n}"}, {title: "MethodSignature#2", input: "interface I {\"a\"()}", output: "interface I {\n \"a\"();\n}"}, {title: "MethodSignature#3", input: "interface I {'a'()}", output: "interface I {\n 'a'();\n}"}, {title: "MethodSignature#4", input: "interface I {0()}", output: "interface I {\n 0();\n}"}, {title: "MethodSignature#5", input: "interface I {0n()}", output: "interface I {\n 0n();\n}"}, {title: "MethodSignature#6", input: "interface I {[a]()}", output: "interface I {\n [a]();\n}"}, {title: "MethodSignature#7", input: "interface I {a?()}", output: "interface I {\n a?();\n}"}, {title: "MethodSignature#8", input: "interface I {a()}", output: "interface I {\n a();\n}"}, {title: "MethodSignature#9", input: "interface I {a(): b}", output: "interface I {\n a(): b;\n}"}, {title: "MethodSignature#10", input: "interface I {a(b): c}", output: "interface I {\n a(b): c;\n}"}, {title: "CallSignature#1", input: "interface I {()}", output: "interface I {\n ();\n}"}, {title: "CallSignature#2", input: "interface I {():a}", output: "interface I {\n (): a;\n}"}, {title: "CallSignature#3", input: "interface I {(p)}", output: "interface I {\n (p);\n}"}, {title: "CallSignature#4", input: "interface I {()}", output: "interface I {\n ();\n}"}, {title: "ConstructSignature#1", input: "interface I {new ()}", output: "interface I {\n new ();\n}"}, {title: "ConstructSignature#2", input: "interface I {new ():a}", output: "interface I {\n new (): a;\n}"}, {title: "ConstructSignature#3", input: "interface I {new (p)}", output: "interface I {\n new (p);\n}"}, {title: "ConstructSignature#4", input: "interface I {new ()}", output: "interface I {\n new ();\n}"}, {title: "IndexSignatureDeclaration#1", input: "interface I {[a]}", output: "interface I {\n [a];\n}"}, {title: "IndexSignatureDeclaration#2", input: "interface I {[a: b]}", output: "interface I {\n [a: b];\n}"}, {title: "IndexSignatureDeclaration#3", input: "interface I {[a: b]: c}", output: "interface I {\n [a: b]: c;\n}"}, {title: "PropertyDeclaration#1", input: "class C {a}", output: "class C {\n a;\n}"}, {title: "PropertyDeclaration#2", input: "class C {readonly a}", output: "class C {\n readonly a;\n}"}, {title: "PropertyDeclaration#3", input: "class C {static a}", output: "class C {\n static a;\n}"}, {title: "PropertyDeclaration#4", input: "class C {accessor a}", output: "class C {\n accessor a;\n}"}, {title: "PropertyDeclaration#5", input: "class C {\"a\"}", output: "class C {\n \"a\";\n}"}, {title: "PropertyDeclaration#6", input: "class C {'a'}", output: "class C {\n 'a';\n}"}, {title: "PropertyDeclaration#7", input: "class C {0}", output: "class C {\n 0;\n}"}, {title: "PropertyDeclaration#8", input: "class C {0n}", output: "class C {\n 0n;\n}"}, {title: "PropertyDeclaration#9", input: "class C {[a]}", output: "class C {\n [a];\n}"}, {title: "PropertyDeclaration#10", input: "class C {#a}", output: "class C {\n #a;\n}"}, {title: "PropertyDeclaration#11", input: "class C {a?}", output: "class C {\n a?;\n}"}, {title: "PropertyDeclaration#12", input: "class C {a!}", output: "class C {\n a!;\n}"}, {title: "PropertyDeclaration#13", input: "class C {a: b}", output: "class C {\n a: b;\n}"}, {title: "PropertyDeclaration#14", input: "class C {a = b}", output: "class C {\n a = b;\n}"}, {title: "PropertyDeclaration#15", input: "class C {@a b}", output: "class C {\n @a\n b;\n}"}, {title: "MethodDeclaration#1", input: "class C {a()}", output: "class C {\n a();\n}"}, {title: "MethodDeclaration#2", input: "class C {\"a\"()}", output: "class C {\n \"a\"();\n}"}, {title: "MethodDeclaration#3", input: "class C {'a'()}", output: "class C {\n 'a'();\n}"}, {title: "MethodDeclaration#4", input: "class C {0()}", output: "class C {\n 0();\n}"}, {title: "MethodDeclaration#5", input: "class C {0n()}", output: "class C {\n 0n();\n}"}, {title: "MethodDeclaration#6", input: "class C {[a]()}", output: "class C {\n [a]();\n}"}, {title: "MethodDeclaration#7", input: "class C {#a()}", output: "class C {\n #a();\n}"}, {title: "MethodDeclaration#8", input: "class C {a?()}", output: "class C {\n a?();\n}"}, {title: "MethodDeclaration#9", input: "class C {a()}", output: "class C {\n a();\n}"}, {title: "MethodDeclaration#10", input: "class C {a(): b}", output: "class C {\n a(): b;\n}"}, {title: "MethodDeclaration#11", input: "class C {a(b): c}", output: "class C {\n a(b): c;\n}"}, {title: "MethodDeclaration#12", input: "class C {a() {} }", output: "class C {\n a() { }\n}"}, {title: "MethodDeclaration#13", input: "class C {@a b() {} }", output: "class C {\n @a\n b() { }\n}"}, {title: "MethodDeclaration#14", input: "class C {static a() {} }", output: "class C {\n static a() { }\n}"}, {title: "MethodDeclaration#15", input: "class C {async a() {} }", output: "class C {\n async a() { }\n}"}, {title: "GetAccessorDeclaration#1", input: "class C {get a()}", output: "class C {\n get a();\n}"}, {title: "GetAccessorDeclaration#2", input: "class C {get \"a\"()}", output: "class C {\n get \"a\"();\n}"}, {title: "GetAccessorDeclaration#3", input: "class C {get 'a'()}", output: "class C {\n get 'a'();\n}"}, {title: "GetAccessorDeclaration#4", input: "class C {get 0()}", output: "class C {\n get 0();\n}"}, {title: "GetAccessorDeclaration#5", input: "class C {get 0n()}", output: "class C {\n get 0n();\n}"}, {title: "GetAccessorDeclaration#6", input: "class C {get [a]()}", output: "class C {\n get [a]();\n}"}, {title: "GetAccessorDeclaration#7", input: "class C {get #a()}", output: "class C {\n get #a();\n}"}, {title: "GetAccessorDeclaration#8", input: "class C {get a(): b}", output: "class C {\n get a(): b;\n}"}, {title: "GetAccessorDeclaration#9", input: "class C {get a(b): c}", output: "class C {\n get a(b): c;\n}"}, {title: "GetAccessorDeclaration#10", input: "class C {get a() {} }", output: "class C {\n get a() { }\n}"}, {title: "GetAccessorDeclaration#11", input: "class C {@a get b() {} }", output: "class C {\n @a\n get b() { }\n}"}, {title: "GetAccessorDeclaration#12", input: "class C {static get a() {} }", output: "class C {\n static get a() { }\n}"}, {title: "SetAccessorDeclaration#1", input: "class C {set a()}", output: "class C {\n set a();\n}"}, {title: "SetAccessorDeclaration#2", input: "class C {set \"a\"()}", output: "class C {\n set \"a\"();\n}"}, {title: "SetAccessorDeclaration#3", input: "class C {set 'a'()}", output: "class C {\n set 'a'();\n}"}, {title: "SetAccessorDeclaration#4", input: "class C {set 0()}", output: "class C {\n set 0();\n}"}, {title: "SetAccessorDeclaration#5", input: "class C {set 0n()}", output: "class C {\n set 0n();\n}"}, {title: "SetAccessorDeclaration#6", input: "class C {set [a]()}", output: "class C {\n set [a]();\n}"}, {title: "SetAccessorDeclaration#7", input: "class C {set #a()}", output: "class C {\n set #a();\n}"}, {title: "SetAccessorDeclaration#8", input: "class C {set a(): b}", output: "class C {\n set a(): b;\n}"}, {title: "SetAccessorDeclaration#9", input: "class C {set a(b): c}", output: "class C {\n set a(b): c;\n}"}, {title: "SetAccessorDeclaration#10", input: "class C {set a() {} }", output: "class C {\n set a() { }\n}"}, {title: "SetAccessorDeclaration#11", input: "class C {@a set b() {} }", output: "class C {\n @a\n set b() { }\n}"}, {title: "SetAccessorDeclaration#12", input: "class C {static set a() {} }", output: "class C {\n static set a() { }\n}"}, {title: "ConstructorDeclaration#1", input: "class C {constructor()}", output: "class C {\n constructor();\n}"}, {title: "ConstructorDeclaration#2", input: "class C {constructor(): b}", output: "class C {\n constructor(): b;\n}"}, {title: "ConstructorDeclaration#3", input: "class C {constructor(b): c}", output: "class C {\n constructor(b): c;\n}"}, {title: "ConstructorDeclaration#4", input: "class C {constructor() {} }", output: "class C {\n constructor() { }\n}"}, {title: "ConstructorDeclaration#5", input: "class C {@a constructor() {} }", output: "class C {\n constructor() { }\n}"}, {title: "ConstructorDeclaration#6", input: "class C {private constructor() {} }", output: "class C {\n private constructor() { }\n}"}, {title: "ClassStaticBlockDeclaration", input: "class C {static { }}", output: "class C {\n static { }\n}"}, {title: "SemicolonClassElement#1", input: "class C {;}", output: "class C {\n ;\n}"}, {title: "ParameterDeclaration#1", input: "function f(a)", output: "function f(a);"}, {title: "ParameterDeclaration#2", input: "function f(a: b)", output: "function f(a: b);"}, {title: "ParameterDeclaration#3", input: "function f(a = b)", output: "function f(a = b);"}, {title: "ParameterDeclaration#4", input: "function f(a?)", output: "function f(a?);"}, {title: "ParameterDeclaration#5", input: "function f(...a)", output: "function f(...a);"}, {title: "ParameterDeclaration#6", input: "function f(this)", output: "function f(this);"}, // {title: "ParameterDeclaration#7", input: "function f(a,)", output: "function f(a,);"}, // TODO: preserve trailing comma after Strada migration {title: "ObjectBindingPattern#1", input: "function f({})", output: "function f({});"}, {title: "ObjectBindingPattern#2", input: "function f({a})", output: "function f({ a });"}, {title: "ObjectBindingPattern#3", input: "function f({a = b})", output: "function f({ a = b });"}, {title: "ObjectBindingPattern#4", input: "function f({a: b})", output: "function f({ a: b });"}, {title: "ObjectBindingPattern#5", input: "function f({a: b = c})", output: "function f({ a: b = c });"}, {title: "ObjectBindingPattern#6", input: "function f({\"a\": b})", output: "function f({ \"a\": b });"}, {title: "ObjectBindingPattern#7", input: "function f({'a': b})", output: "function f({ 'a': b });"}, {title: "ObjectBindingPattern#8", input: "function f({0: b})", output: "function f({ 0: b });"}, {title: "ObjectBindingPattern#9", input: "function f({[a]: b})", output: "function f({ [a]: b });"}, {title: "ObjectBindingPattern#10", input: "function f({...a})", output: "function f({ ...a });"}, {title: "ObjectBindingPattern#11", input: "function f({a: {}})", output: "function f({ a: {} });"}, {title: "ObjectBindingPattern#12", input: "function f({a: []})", output: "function f({ a: [] });"}, {title: "ArrayBindingPattern#1", input: "function f([])", output: "function f([]);"}, {title: "ArrayBindingPattern#2", input: "function f([,])", output: "function f([,]);"}, {title: "ArrayBindingPattern#3", input: "function f([a])", output: "function f([a]);"}, {title: "ArrayBindingPattern#4", input: "function f([a, b])", output: "function f([a, b]);"}, {title: "ArrayBindingPattern#5", input: "function f([a, , b])", output: "function f([a, , b]);"}, {title: "ArrayBindingPattern#6", input: "function f([a = b])", output: "function f([a = b]);"}, {title: "ArrayBindingPattern#7", input: "function f([...a])", output: "function f([...a]);"}, {title: "ArrayBindingPattern#8", input: "function f([{}])", output: "function f([{}]);"}, {title: "ArrayBindingPattern#9", input: "function f([[]])", output: "function f([[]]);"}, {title: "TypeParameterDeclaration#1", input: "function f();", output: "function f();"}, {title: "TypeParameterDeclaration#2", input: "function f();", output: "function f();"}, {title: "TypeParameterDeclaration#3", input: "function f();", output: "function f();"}, {title: "TypeParameterDeclaration#4", input: "function f();", output: "function f();"}, {title: "TypeParameterDeclaration#5", input: "function f();", output: "function f();"}, {title: "TypeParameterDeclaration#6", input: "function f();", output: "function f();"}, // {title: "TypeParameterDeclaration#7", input: "function f();", output: "function f();"}, // TODO: preserve trailing comma after Strada migration {title: "JsxElement1", input: "", output: ";", jsx: true}, {title: "JsxElement2", input: "", output: ";", jsx: true}, {title: "JsxElement3", input: "", output: ";", jsx: true}, {title: "JsxElement4", input: "", output: ";", jsx: true}, {title: "JsxElement5", input: ">", output: ">;", jsx: true}, {title: "JsxElement6", input: "", output: ";", jsx: true}, {title: "JsxElement7", input: "b", output: "b;", jsx: true}, {title: "JsxElement8", input: "{b}", output: "{b};", jsx: true}, {title: "JsxElement9", input: "", output: ";", jsx: true}, {title: "JsxElement10", input: "", output: ";", jsx: true}, {title: "JsxElement11", input: "<>", output: "<>;", jsx: true}, {title: "JsxSelfClosingElement1", input: "", output: ";", jsx: true}, {title: "JsxSelfClosingElement2", input: "", output: ";", jsx: true}, {title: "JsxSelfClosingElement3", input: "", output: ";", jsx: true}, {title: "JsxSelfClosingElement4", input: "", output: ";", jsx: true}, {title: "JsxSelfClosingElement5", input: " />", output: " />;", jsx: true}, {title: "JsxSelfClosingElement6", input: "", output: ";", jsx: true}, {title: "JsxFragment1", input: "<>", output: "<>;", jsx: true}, {title: "JsxFragment2", input: "<>b", output: "<>b;", jsx: true}, {title: "JsxFragment3", input: "<>{b}", output: "<>{b};", jsx: true}, {title: "JsxFragment4", input: "<>", output: "<>;", jsx: true}, {title: "JsxFragment5", input: "<>", output: "<>;", jsx: true}, {title: "JsxFragment6", input: "<><>", output: "<><>;", jsx: true}, {title: "JsxAttribute1", input: "", output: ";", jsx: true}, {title: "JsxAttribute2", input: "", output: ";", jsx: true}, {title: "JsxAttribute3", input: "", output: ";", jsx: true}, {title: "JsxAttribute4", input: "", output: ";", jsx: true}, {title: "JsxAttribute5", input: "", output: ";", jsx: true}, {title: "JsxAttribute6", input: "/>", output: "/>;", jsx: true}, {title: "JsxAttribute7", input: "/>", output: "/>;", jsx: true}, {title: "JsxAttribute8", input: "/>", output: "/>;", jsx: true}, {title: "JsxSpreadAttribute", input: "", output: ";", jsx: true}, } for _, rec := range data { t.Run(rec.title, func(t *testing.T) { t.Parallel() file := parsetestutil.ParseTypeScript(rec.input, rec.jsx) parsetestutil.CheckDiagnostics(t, file) emittestutil.CheckEmit(t, nil, file, rec.output) }) } } func TestParenthesizeDecorator(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewClassDeclaration( factory.NewModifierList( []*ast.Node{ factory.NewDecorator( factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindPlusToken), factory.NewIdentifier("b"), ), ), }, ), factory.NewIdentifier("C"), nil, nil, factory.NewNodeList([]*ast.Node{}), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "@(a + b)\nclass C {\n}") } func TestParenthesizeComputedPropertyName(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewClassDeclaration( nil, /*modifiers*/ factory.NewIdentifier("C"), nil, /*typeParameters*/ nil, /*heritageClauses*/ factory.NewNodeList([]*ast.Node{ factory.NewPropertyDeclaration( nil, /*modifiers*/ factory.NewComputedPropertyName( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("b"), ), ), nil, /*postfixToken*/ nil, /*typeNode*/ nil, /*initializer*/ ), }), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "class C {\n [(a, b)];\n}") } func TestParenthesizeArrayLiteral(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewArrayLiteralExpression( factory.NewNodeList( []*ast.Node{ // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("b"), ), }, ), false, /*multiLine*/ ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "[(a, b)];") } func TestParenthesizePropertyAccess1(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewPropertyAccessExpression( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("b"), ), nil, /*questionDotToken*/ factory.NewIdentifier("c"), ast.NodeFlagsNone, ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b).c;") } func TestParenthesizePropertyAccess2(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewPropertyAccessExpression( // will be parenthesized on emit: factory.NewPropertyAccessExpression( factory.NewIdentifier("a"), factory.NewToken(ast.KindQuestionDotToken), factory.NewIdentifier("b"), ast.NodeFlagsOptionalChain, ), nil, /*questionDotToken*/ factory.NewIdentifier("c"), ast.NodeFlagsNone, ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a?.b).c;") } func TestParenthesizePropertyAccess3(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewPropertyAccessExpression( // will be parenthesized on emit: factory.NewNewExpression( factory.NewIdentifier("a"), nil, /*typeArguments*/ nil, /*arguments*/ ), nil, /*questionDotToken*/ factory.NewIdentifier("b"), ast.NodeFlagsNone, ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(new a).b;") } func TestParenthesizeElementAccess1(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewElementAccessExpression( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("b"), ), nil, /*questionDotToken*/ factory.NewIdentifier("c"), ast.NodeFlagsNone, ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b)[c];") } func TestParenthesizeElementAccess2(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewElementAccessExpression( // will be parenthesized on emit: factory.NewPropertyAccessExpression( factory.NewIdentifier("a"), factory.NewToken(ast.KindQuestionDotToken), factory.NewIdentifier("b"), ast.NodeFlagsOptionalChain, ), nil, /*questionDotToken*/ factory.NewIdentifier("c"), ast.NodeFlagsNone, ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a?.b)[c];") } func TestParenthesizeElementAccess3(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewElementAccessExpression( // will be parenthesized on emit: factory.NewNewExpression( factory.NewIdentifier("a"), nil, /*typeArguments*/ nil, /*arguments*/ ), nil, /*questionDotToken*/ factory.NewIdentifier("b"), ast.NodeFlagsNone, ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(new a)[b];") } func TestParenthesizeCall1(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewCallExpression( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("b"), ), nil, /*questionDotToken*/ nil, /*typeArguments*/ factory.NewNodeList([]*ast.Node{}), ast.NodeFlagsNone, ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b)();") } func TestParenthesizeCall2(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewCallExpression( // will be parenthesized on emit: factory.NewPropertyAccessExpression( factory.NewIdentifier("a"), factory.NewToken(ast.KindQuestionDotToken), factory.NewIdentifier("b"), ast.NodeFlagsOptionalChain, ), nil, /*questionDotToken*/ nil, /*typeArguments*/ factory.NewNodeList([]*ast.Node{}), ast.NodeFlagsNone, ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a?.b)();") } func TestParenthesizeCall3(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewCallExpression( // will be parenthesized on emit: factory.NewNewExpression( factory.NewIdentifier("C"), nil, /*typeArguments*/ nil, /*arguments*/ ), nil, /*questionDotToken*/ nil, /*typeArguments*/ factory.NewNodeList([]*ast.Node{}), ast.NodeFlagsNone, ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(new C)();") } func TestParenthesizeCall4(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewCallExpression( factory.NewIdentifier("a"), nil, /*questionDotToken*/ nil, /*typeArguments*/ factory.NewNodeList([]*ast.Node{ factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("b"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("c"), ), }), ast.NodeFlagsNone, ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "a((b, c));") } func TestParenthesizeNew1(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewNewExpression( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("b"), ), nil, /*typeArguments*/ factory.NewNodeList([]*ast.Node{}), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "new (a, b)();") } func TestParenthesizeNew2(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewNewExpression( // will be parenthesized on emit: factory.NewCallExpression( factory.NewIdentifier("C"), nil, /*questionDotToken*/ nil, /*typeArguments*/ factory.NewNodeList([]*ast.Node{}), ast.NodeFlagsNone, ), nil, /*typeArguments*/ nil, /*arguments*/ ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "new (C());") } func TestParenthesizeNew3(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewNewExpression( factory.NewIdentifier("C"), nil, /*typeArguments*/ factory.NewNodeList([]*ast.Node{ factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("b"), ), }), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "new C((a, b));") } func TestParenthesizeTaggedTemplate1(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewTaggedTemplateExpression( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("b"), ), nil, /*questionDotToken*/ nil, /*typeArguments*/ factory.NewNoSubstitutionTemplateLiteral(""), ast.NodeFlagsNone, ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b) ``;") } func TestParenthesizeTaggedTemplate2(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewTaggedTemplateExpression( // will be parenthesized on emit: factory.NewPropertyAccessExpression( factory.NewIdentifier("a"), factory.NewToken(ast.KindQuestionDotToken), factory.NewIdentifier("b"), ast.NodeFlagsOptionalChain, ), nil, /*questionDotToken*/ nil, /*typeArguments*/ factory.NewNoSubstitutionTemplateLiteral(""), ast.NodeFlagsNone, ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a?.b) ``;") } func TestParenthesizeTypeAssertion1(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewTypeAssertion( factory.NewTypeReferenceNode( factory.NewIdentifier("T"), nil, /*typeArguments*/ ), // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindPlusToken), factory.NewIdentifier("b"), ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a + b);") } func TestParenthesizeArrowFunction1(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewArrowFunction( nil, /*modifiers*/ nil, /*typeParameters*/ factory.NewNodeList([]*ast.Node{}), nil, /*returnType*/ nil, /*fullSignature*/ factory.NewToken(ast.KindEqualsGreaterThanToken), // will be parenthesized on emit: factory.NewObjectLiteralExpression( factory.NewNodeList([]*ast.Node{}), false, /*multiLine*/ ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "() => ({});") } func TestParenthesizeArrowFunction2(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewArrowFunction( nil, /*modifiers*/ nil, /*typeParameters*/ factory.NewNodeList([]*ast.Node{}), nil, /*returnType*/ nil, /*fullSignature*/ factory.NewToken(ast.KindEqualsGreaterThanToken), // will be parenthesized on emit: factory.NewPropertyAccessExpression( factory.NewObjectLiteralExpression( factory.NewNodeList([]*ast.Node{}), false, /*multiLine*/ ), nil, /*questionDotToken*/ factory.NewIdentifier("a"), ast.NodeFlagsNone, ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "() => ({}.a);") } func TestParenthesizeDelete(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewDeleteExpression( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindPlusToken), factory.NewIdentifier("b"), ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "delete (a + b);") } func TestParenthesizeVoid(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewVoidExpression( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindPlusToken), factory.NewIdentifier("b"), ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "void (a + b);") } func TestParenthesizeTypeOf(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewTypeOfExpression( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindPlusToken), factory.NewIdentifier("b"), ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "typeof (a + b);") } func TestParenthesizeAwait(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewAwaitExpression( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindPlusToken), factory.NewIdentifier("b"), ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "await (a + b);") } func isBinaryOperator(token ast.Kind) bool { switch token { case ast.KindCommaToken, ast.KindLessThanToken, ast.KindGreaterThanToken, ast.KindLessThanEqualsToken, ast.KindGreaterThanEqualsToken, ast.KindEqualsEqualsToken, ast.KindEqualsEqualsEqualsToken, ast.KindExclamationEqualsToken, ast.KindExclamationEqualsEqualsToken, ast.KindPlusToken, ast.KindMinusToken, ast.KindAsteriskToken, ast.KindAsteriskAsteriskToken, ast.KindSlashToken, ast.KindPercentToken, ast.KindLessThanLessThanToken, ast.KindGreaterThanGreaterThanToken, ast.KindGreaterThanGreaterThanGreaterThanToken, ast.KindAmpersandToken, ast.KindBarToken, ast.KindCaretToken, ast.KindAmpersandAmpersandToken, ast.KindBarBarToken, ast.KindQuestionQuestionToken, ast.KindEqualsToken, ast.KindPlusEqualsToken, ast.KindMinusEqualsToken, ast.KindAsteriskEqualsToken, ast.KindAsteriskAsteriskEqualsToken, ast.KindSlashEqualsToken, ast.KindPercentEqualsToken, ast.KindLessThanLessThanEqualsToken, ast.KindGreaterThanGreaterThanEqualsToken, ast.KindGreaterThanGreaterThanGreaterThanEqualsToken, ast.KindAmpersandEqualsToken, ast.KindBarEqualsToken, ast.KindBarBarEqualsToken, ast.KindAmpersandAmpersandEqualsToken, ast.KindQuestionQuestionEqualsToken, ast.KindCaretEqualsToken, ast.KindInKeyword, ast.KindInstanceOfKeyword: return true } return false } func makeSide(label string, kind ast.Kind, factory *ast.NodeFactory) *ast.Node { switch { case kind == ast.KindIdentifier || kind == ast.KindUnknown: return factory.NewIdentifier(label) case kind == ast.KindArrowFunction: return factory.NewArrowFunction( nil, /*modifiers*/ nil, /*typeParameters*/ factory.NewNodeList([]*ast.Node{}), nil, /*returnType*/ nil, /*fullSignature*/ factory.NewToken(ast.KindEqualsGreaterThanToken), factory.NewBlock(factory.NewNodeList([]*ast.Node{}), false /*multiLine*/), ) case isBinaryOperator(kind): return factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier(label+"l"), nil, /*typeNode*/ factory.NewToken(kind), factory.NewIdentifier(label+"r"), ) default: panic("unsupported kind") } } func TestParenthesizeBinary(t *testing.T) { t.Parallel() data := []struct { left ast.Kind operator ast.Kind right ast.Kind output string }{ {operator: ast.KindCommaToken, output: "l, r"}, {operator: ast.KindCommaToken, left: ast.KindPlusToken, output: "ll + lr, r"}, {operator: ast.KindAsteriskToken, left: ast.KindPlusToken, output: "(ll + lr) * r"}, {operator: ast.KindAsteriskToken, right: ast.KindPlusToken, output: "l * (rl + rr)"}, {operator: ast.KindPlusToken, left: ast.KindAsteriskToken, output: "ll * lr + r"}, {operator: ast.KindPlusToken, right: ast.KindAsteriskToken, output: "l + rl * rr"}, {operator: ast.KindSlashToken, left: ast.KindAsteriskToken, output: "ll * lr / r"}, {operator: ast.KindSlashToken, left: ast.KindAsteriskAsteriskToken, output: "ll ** lr / r"}, {operator: ast.KindAsteriskAsteriskToken, left: ast.KindAsteriskToken, output: "(ll * lr) ** r"}, {operator: ast.KindAsteriskAsteriskToken, left: ast.KindAsteriskAsteriskToken, output: "(ll ** lr) ** r"}, {operator: ast.KindAsteriskToken, right: ast.KindAsteriskToken, output: "l * rl * rr"}, {operator: ast.KindBarToken, right: ast.KindBarToken, output: "l | rl | rr"}, {operator: ast.KindAmpersandToken, right: ast.KindAmpersandToken, output: "l & rl & rr"}, {operator: ast.KindCaretToken, right: ast.KindCaretToken, output: "l ^ rl ^ rr"}, {operator: ast.KindAmpersandAmpersandToken, right: ast.KindArrowFunction, output: "l && (() => { })"}, } for _, rec := range data { t.Run(rec.output, func(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewBinaryExpression( nil, /*modifiers*/ makeSide("l", rec.left, &factory), nil, /*typeNode*/ factory.NewToken(rec.operator), makeSide("r", rec.right, &factory), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), rec.output+";") }) } } func TestParenthesizeConditional1(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewConditionalExpression( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("b"), ), factory.NewToken(ast.KindQuestionToken), factory.NewIdentifier("c"), factory.NewToken(ast.KindColonToken), factory.NewIdentifier("d"), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b) ? c : d;") } func TestParenthesizeConditional2(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewConditionalExpression( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindEqualsToken), factory.NewIdentifier("b"), ), factory.NewToken(ast.KindQuestionToken), factory.NewIdentifier("c"), factory.NewToken(ast.KindColonToken), factory.NewIdentifier("d"), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a = b) ? c : d;") } func TestParenthesizeConditional3(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewConditionalExpression( // will be parenthesized on emit: factory.NewArrowFunction( nil, /*modifiers*/ nil, /*typeParameters*/ factory.NewNodeList([]*ast.Node{}), nil, /*returnType*/ nil, /*fullSignature*/ factory.NewToken(ast.KindEqualsGreaterThanToken), factory.NewBlock( factory.NewNodeList([]*ast.Node{}), false, /*multiLine*/ ), ), factory.NewToken(ast.KindQuestionToken), factory.NewIdentifier("a"), factory.NewToken(ast.KindColonToken), factory.NewIdentifier("b"), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(() => { }) ? a : b;") } func TestParenthesizeConditional4(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewConditionalExpression( // will be parenthesized on emit: factory.NewYieldExpression(nil, nil), factory.NewToken(ast.KindQuestionToken), factory.NewIdentifier("a"), factory.NewToken(ast.KindColonToken), factory.NewIdentifier("b"), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(yield) ? a : b;") } func TestParenthesizeConditional5(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewConditionalExpression( factory.NewIdentifier("a"), factory.NewToken(ast.KindQuestionToken), // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("b"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("c"), ), factory.NewToken(ast.KindColonToken), factory.NewIdentifier("d"), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "a ? (b, c) : d;") } func TestParenthesizeConditional6(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewConditionalExpression( factory.NewIdentifier("a"), factory.NewToken(ast.KindQuestionToken), factory.NewIdentifier("b"), factory.NewToken(ast.KindColonToken), // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("c"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("d"), ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "a ? b : (c, d);") } func TestParenthesizeYield1(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewYieldExpression( nil, /*asteriskToken*/ // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("b"), ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "yield (a, b);") } // !!! test ASI avoidance from emitExpressionNoASI ////func TestParenthesizeYield2(t *testing.T) { ////} func TestParenthesizeSpreadElement1(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewArrayLiteralExpression( factory.NewNodeList( []*ast.Node{ factory.NewSpreadElement( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("b"), ), ), }, ), false, /*multiLine*/ ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "[...(a, b)];") } func TestParenthesizeSpreadElement2(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewCallExpression( factory.NewIdentifier("a"), nil, /*questionDotToken*/ nil, /*typeArguments*/ factory.NewNodeList( []*ast.Node{ factory.NewSpreadElement( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("b"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("c"), ), ), }, ), ast.NodeFlagsNone, ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "a(...(b, c));") } func TestParenthesizeSpreadElement3(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewNewExpression( factory.NewIdentifier("a"), nil, /*typeArguments*/ factory.NewNodeList( []*ast.Node{ factory.NewSpreadElement( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("b"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("c"), ), ), }, ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "new a(...(b, c));") } func TestParenthesizeExpressionWithTypeArguments(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewExpressionWithTypeArguments( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("b"), ), factory.NewNodeList( []*ast.Node{ factory.NewTypeReferenceNode( factory.NewIdentifier("c"), nil, ), }, ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b);") } func TestParenthesizeAsExpression(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewAsExpression( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("b"), ), factory.NewTypeReferenceNode( factory.NewIdentifier("c"), nil, ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b) as c;") } func TestParenthesizeSatisfiesExpression(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewSatisfiesExpression( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("b"), ), factory.NewTypeReferenceNode( factory.NewIdentifier("c"), nil, ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b) satisfies c;") } func TestParenthesizeNonNullExpression(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewNonNullExpression( // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("b"), ), ast.NodeFlagsNone, ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(a, b)!;") } func TestParenthesizeExpressionStatement1(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewObjectLiteralExpression( factory.NewNodeList( []*ast.Node{}, ), false, /*multiLine*/ ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "({});") } func TestParenthesizeExpressionStatement2(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewFunctionExpression( nil, /*modifiers*/ nil, /*asteriskToken*/ nil, /*name*/ nil, /*typeParameters*/ factory.NewNodeList( []*ast.Node{}, ), nil, /*returnType*/ nil, /*fullSignature*/ factory.NewBlock( factory.NewNodeList([]*ast.Node{}), false, /*multiLine*/ ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(function () { });") } func TestParenthesizeExpressionStatement3(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExpressionStatement( factory.NewClassExpression( nil, /*modifiers*/ nil, /*name*/ nil, /*typeParameters*/ nil, /*heritageClauses*/ factory.NewNodeList( []*ast.Node{}, ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "(class {\n});") } func TestParenthesizeExpressionDefault1(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExportAssignment( nil, /*modifiers*/ false, /*isExportEquals*/ nil, /*typeNode*/ // will be parenthesized on emit: factory.NewClassExpression( nil, /*modifiers*/ nil, /*name*/ nil, /*typeParameters*/ nil, /*heritageClauses*/ factory.NewNodeList( []*ast.Node{}, ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "export default (class {\n});") } func TestParenthesizeExpressionDefault2(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExportAssignment( nil, /*modifiers*/ false, /*isExportEquals*/ nil, /*typeNode*/ // will be parenthesized on emit: factory.NewFunctionExpression( nil, /*modifiers*/ nil, /*asteriskToken*/ nil, /*name*/ nil, /*typeParameters*/ factory.NewNodeList( []*ast.Node{}, ), nil, /*returnType*/ nil, /*fullSignature*/ factory.NewBlock( factory.NewNodeList( []*ast.Node{}, ), false, /*multiLine*/ ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "export default (function () { });") } func TestParenthesizeExpressionDefault3(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewExportAssignment( nil, /*modifiers*/ false, /*isExportEquals*/ nil, /*typeNode*/ // will be parenthesized on emit: factory.NewBinaryExpression( nil, /*modifiers*/ factory.NewIdentifier("a"), nil, /*typeNode*/ factory.NewToken(ast.KindCommaToken), factory.NewIdentifier("b"), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "export default (a, b);") } func TestParenthesizeArrayType(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewTypeAliasDeclaration( nil, /*modifiers*/ factory.NewIdentifier("_"), /*name*/ nil, /*typeParameters*/ factory.NewArrayTypeNode( // will be parenthesized on emit: factory.NewUnionTypeNode( factory.NewNodeList( []*ast.Node{ factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), }, ), ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = (a | b)[];") } func TestParenthesizeOptionalType(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewTypeAliasDeclaration( nil, /*modifiers*/ factory.NewIdentifier("_"), /*name*/ nil, /*typeParameters*/ factory.NewTupleTypeNode( factory.NewNodeList( []*ast.Node{ factory.NewOptionalTypeNode( // will be parenthesized on emit: factory.NewUnionTypeNode( factory.NewNodeList( []*ast.Node{ factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), }, ), ), ), }, ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = [\n (a | b)?\n];") } func TestParenthesizeUnionType1(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewTypeAliasDeclaration( nil, /*modifiers*/ factory.NewIdentifier("_"), /*name*/ nil, /*typeParameters*/ factory.NewUnionTypeNode( factory.NewNodeList( []*ast.Node{ factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), // will be parenthesized on emit: factory.NewFunctionTypeNode( nil, /*typeParameters*/ factory.NewNodeList( []*ast.Node{}, ), factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), ), }, ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = a | (() => b);") } func TestParenthesizeUnionType2(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewTypeAliasDeclaration( nil, /*modifiers*/ factory.NewIdentifier("_"), /*name*/ nil, /*typeParameters*/ factory.NewUnionTypeNode( factory.NewNodeList( []*ast.Node{ // will be parenthesized on emit: factory.NewInferTypeNode( factory.NewTypeParameterDeclaration( nil, factory.NewIdentifier("a"), factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), nil, /*defaultType*/ ), ), factory.NewTypeReferenceNode(factory.NewIdentifier("c"), nil /*typeArguments*/), }, ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = (infer a extends b) | c;") } func TestParenthesizeIntersectionType(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewTypeAliasDeclaration( nil, /*modifiers*/ factory.NewIdentifier("_"), /*name*/ nil, /*typeParameters*/ factory.NewIntersectionTypeNode( factory.NewNodeList( []*ast.Node{ factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), // will be parenthesized on emit: factory.NewUnionTypeNode( factory.NewNodeList( []*ast.Node{ factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), factory.NewTypeReferenceNode(factory.NewIdentifier("c"), nil /*typeArguments*/), }, ), ), }, ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = a & (b | c);") } func TestParenthesizeReadonlyTypeOperator1(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewTypeAliasDeclaration( nil, /*modifiers*/ factory.NewIdentifier("_"), /*name*/ nil, /*typeParameters*/ factory.NewTypeOperatorNode( ast.KindReadonlyKeyword, // will be parenthesized on emit: factory.NewUnionTypeNode( factory.NewNodeList( []*ast.Node{ factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), }, ), ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = readonly (a | b);") } func TestParenthesizeReadonlyTypeOperator2(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewTypeAliasDeclaration( nil, /*modifiers*/ factory.NewIdentifier("_"), /*name*/ nil, /*typeParameters*/ factory.NewTypeOperatorNode( ast.KindReadonlyKeyword, // will be parenthesized on emit: factory.NewTypeOperatorNode( ast.KindKeyOfKeyword, factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = readonly (keyof a);") } func TestParenthesizeKeyofTypeOperator(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewTypeAliasDeclaration( nil, /*modifiers*/ factory.NewIdentifier("_"), /*name*/ nil, /*typeParameters*/ factory.NewTypeOperatorNode( ast.KindKeyOfKeyword, // will be parenthesized on emit: factory.NewUnionTypeNode( factory.NewNodeList( []*ast.Node{ factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), }, ), ), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = keyof (a | b);") } func TestParenthesizeIndexedAccessType(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewTypeAliasDeclaration( nil, /*modifiers*/ factory.NewIdentifier("_"), /*name*/ nil, /*typeParameters*/ factory.NewIndexedAccessTypeNode( // will be parenthesized on emit: factory.NewUnionTypeNode( factory.NewNodeList( []*ast.Node{ factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), }, ), ), factory.NewTypeReferenceNode(factory.NewIdentifier("c"), nil /*typeArguments*/), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = (a | b)[c];") } func TestParenthesizeConditionalType1(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewTypeAliasDeclaration( nil, /*modifiers*/ factory.NewIdentifier("_"), /*name*/ nil, /*typeParameters*/ factory.NewConditionalTypeNode( // will be parenthesized on emit: factory.NewFunctionTypeNode( nil, /*typeParameters*/ factory.NewNodeList( []*ast.Node{}, ), factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), ), factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), factory.NewTypeReferenceNode(factory.NewIdentifier("c"), nil /*typeArguments*/), factory.NewTypeReferenceNode(factory.NewIdentifier("d"), nil /*typeArguments*/), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = (() => a) extends b ? c : d;") } func TestParenthesizeConditionalType2(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewTypeAliasDeclaration( nil, /*modifiers*/ factory.NewIdentifier("_"), /*name*/ nil, /*typeParameters*/ factory.NewConditionalTypeNode( factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), // will be parenthesized on emit: factory.NewConditionalTypeNode( factory.NewTypeReferenceNode(factory.NewIdentifier("b"), nil /*typeArguments*/), factory.NewTypeReferenceNode(factory.NewIdentifier("c"), nil /*typeArguments*/), factory.NewTypeReferenceNode(factory.NewIdentifier("d"), nil /*typeArguments*/), factory.NewTypeReferenceNode(factory.NewIdentifier("e"), nil /*typeArguments*/), ), factory.NewTypeReferenceNode(factory.NewIdentifier("f"), nil /*typeArguments*/), factory.NewTypeReferenceNode(factory.NewIdentifier("g"), nil /*typeArguments*/), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = a extends (b extends c ? d : e) ? f : g;") } func TestParenthesizeConditionalType3(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList( []*ast.Node{ factory.NewTypeAliasDeclaration( nil, /*modifiers*/ factory.NewIdentifier("_"), /*name*/ nil, /*typeParameters*/ factory.NewConditionalTypeNode( factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), factory.NewFunctionTypeNode( nil, /*typeParameters*/ factory.NewNodeList( []*ast.Node{}, ), // will be parenthesized on emit: factory.NewInferTypeNode( factory.NewTypeParameterDeclaration( nil, factory.NewIdentifier("b"), factory.NewTypeReferenceNode(factory.NewIdentifier("c"), nil /*typeArguments*/), nil, /*defaultType*/ ), ), ), factory.NewTypeReferenceNode(factory.NewIdentifier("d"), nil /*typeArguments*/), factory.NewTypeReferenceNode(factory.NewIdentifier("e"), nil /*typeArguments*/), ), ), }, ), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = a extends () => (infer b extends c) ? d : e;") } func TestParenthesizeConditionalType4(t *testing.T) { t.Parallel() var factory ast.NodeFactory file := factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", factory.NewNodeList([]*ast.Node{ factory.NewTypeAliasDeclaration( nil, /*modifiers*/ factory.NewIdentifier("_"), /*name*/ nil, /*typeParameters*/ factory.NewConditionalTypeNode( factory.NewTypeReferenceNode(factory.NewIdentifier("a"), nil /*typeArguments*/), factory.NewFunctionTypeNode( nil, /*typeParameters*/ factory.NewNodeList( []*ast.Node{}, ), // will be parenthesized on emit: factory.NewUnionTypeNode( factory.NewNodeList( []*ast.Node{ factory.NewInferTypeNode( factory.NewTypeParameterDeclaration( nil, factory.NewIdentifier("b"), factory.NewTypeReferenceNode(factory.NewIdentifier("c"), nil /*typeArguments*/), nil, /*defaultType*/ ), ), factory.NewTypeReferenceNode(factory.NewIdentifier("d"), nil /*typeArguments*/), }, ), ), ), factory.NewTypeReferenceNode(factory.NewIdentifier("e"), nil /*typeArguments*/), factory.NewTypeReferenceNode(factory.NewIdentifier("f"), nil /*typeArguments*/), ), ), }), factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, nil, file.AsSourceFile(), "type _ = a extends () => (infer b extends c) | d ? e : f;") } func TestNameGeneration(t *testing.T) { t.Parallel() ec := printer.NewEmitContext() file := ec.Factory.NewSourceFile(ast.SourceFileParseOptions{FileName: "/file.ts", Path: "/file.ts"}, "", ec.Factory.NewNodeList([]*ast.Node{ ec.Factory.NewVariableStatement(nil, ec.Factory.NewVariableDeclarationList( ast.NodeFlagsNone, ec.Factory.NewNodeList([]*ast.Node{ ec.Factory.NewVariableDeclaration(ec.Factory.NewTempVariable(), nil, nil, nil), }), )), ec.Factory.NewFunctionDeclaration( nil, nil, ec.Factory.NewIdentifier("f"), nil, ec.Factory.NewNodeList([]*ast.Node{}), nil, nil, ec.Factory.NewBlock(ec.Factory.NewNodeList([]*ast.Node{ ec.Factory.NewVariableStatement(nil, ec.Factory.NewVariableDeclarationList( ast.NodeFlagsNone, ec.Factory.NewNodeList([]*ast.Node{ ec.Factory.NewVariableDeclaration(ec.Factory.NewTempVariable(), nil, nil, nil), }), )), }), true), ), }), ec.Factory.NewToken(ast.KindEndOfFile)) parsetestutil.MarkSyntheticRecursive(file) emittestutil.CheckEmit(t, ec, file.AsSourceFile(), "var _a;\nfunction f() {\n var _a;\n}") } func TestNoTrailingCommaAfterTransform(t *testing.T) { t.Parallel() file := parsetestutil.ParseTypeScript("[a!]", false /*jsx*/) emitContext := printer.NewEmitContext() var visitor *ast.NodeVisitor visitor = emitContext.NewNodeVisitor(func(node *ast.Node) *ast.Node { switch node.Kind { case ast.KindNonNullExpression: node = node.AsNonNullExpression().Expression default: node = node.VisitEachChild(visitor) } return node }) file = visitor.VisitSourceFile(file) emittestutil.CheckEmit(t, emitContext, file.AsSourceFile(), "[a];") } func TestTrailingCommaAfterTransform(t *testing.T) { t.Parallel() file := parsetestutil.ParseTypeScript("[a!,]", false /*jsx*/) emitContext := printer.NewEmitContext() var visitor *ast.NodeVisitor visitor = emitContext.NewNodeVisitor(func(node *ast.Node) *ast.Node { switch node.Kind { case ast.KindNonNullExpression: node = node.AsNonNullExpression().Expression default: node = node.VisitEachChild(visitor) } return node }) file = visitor.VisitSourceFile(file) emittestutil.CheckEmit(t, emitContext, file.AsSourceFile(), "[a,];") } func TestPartiallyEmittedExpression(t *testing.T) { t.Parallel() compilerOptions := &core.CompilerOptions{} file := parsetestutil.ParseTypeScript(`return ((container.parent .left as PropertyAccessExpression) .expression as PropertyAccessExpression) .expression;`, false /*jsx*/) emitContext := printer.NewEmitContext() file = tstransforms.NewTypeEraserTransformer(&transformers.TransformOptions{CompilerOptions: compilerOptions, Context: emitContext}).TransformSourceFile(file) emittestutil.CheckEmit(t, emitContext, file.AsSourceFile(), `return container.parent .left .expression .expression;`) }