diff --git a/tsc/internal/printer/printer.go b/tsc/internal/printer/printer.go index 46bb47c25ebad..28e6dbf572fc1 100644 --- a/tsc/internal/printer/printer.go +++ b/tsc/internal/printer/printer.go @@ -2593,8 +2593,9 @@ func (p *Printer) emitNewExpression(node *ast.NewExpression) { state := p.enterNode(node.AsNode()) p.emitToken(ast.KindNewKeyword, node.Pos(), WriteKindKeyword, node.AsNode()) p.writeSpace() - if ast.SkipPartiallyEmittedExpressions(node.Expression).Kind == ast.KindCallExpression { - // Parenthesize `C()` inside of a NewExpression so it is treated as `new (C())` and not `new C()` + if hasUnparenthesizedCallAtStartOfMemberChain(node.Expression) { + // Parenthesize `C()` or `C().D` inside of a NewExpression so it is treated as `new (C())` or `new (C().D)` + // and not `new C()` or `new C().D` p.emitExpression(node.Expression, ast.OperatorPrecedenceParentheses) } else { p.emitExpression(node.Expression, ast.OperatorPrecedenceMember) diff --git a/tsc/internal/printer/utilities.go b/tsc/internal/printer/utilities.go index 096b4d27afe1f..3ca7d9f50401a 100644 --- a/tsc/internal/printer/utilities.go +++ b/tsc/internal/printer/utilities.go @@ -622,6 +622,30 @@ func isNewExpressionWithoutArguments(node *ast.Node) bool { return node.Kind == ast.KindNewExpression && node.ArgumentList() == nil } +// Determines whether the expression of a NewExpression would be emitted with an unparenthesized CallExpression at the +// start of its member chain, such as `C()` in `C().D`. Such an expression must be parenthesized when used as the +// expression of a NewExpression, since `new C().D()` would be parsed as `(new C()).D()` rather than `new (C().D)()`. +func hasUnparenthesizedCallAtStartOfMemberChain(node *ast.Expression) bool { + for { + node = ast.SkipPartiallyEmittedExpressions(node) + if ast.GetExpressionPrecedence(node) < ast.OperatorPrecedenceMember { + // Each expression in the member chain is emitted with member precedence, so this expression will already + // be parenthesized, which ends the member chain. + return false + } + switch node.Kind { + case ast.KindCallExpression: + return true + case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression, ast.KindExpressionWithTypeArguments: + node = node.Expression() + case ast.KindTaggedTemplateExpression: + node = node.AsTaggedTemplateExpression().Tag + default: + return false + } + } +} + func isBinaryOperation(node *ast.Node, token ast.Kind) bool { node = ast.SkipPartiallyEmittedExpressions(node) return node.Kind == ast.KindBinaryExpression && diff --git a/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParentheses.js b/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParentheses.js new file mode 100644 index 0000000000000..6ef4c83fff06d --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParentheses.js @@ -0,0 +1,60 @@ +//// [tests/cases/compiler/newExpressionCalleeParentheses.ts] //// + +//// [newExpressionCalleeParentheses.ts] +declare function getPlugins(): any; +declare function getPluginsGeneric(): T; +declare function getPluginsFactory(): () => T; +declare const obj: any; +declare class Foo { [key: string]: any } + +// Parentheses are required when a call is at the start of the member chain of a `new` expression's target +const a1 = new (getPlugins() as any).Linker(); +const a2 = new (getPlugins() satisfies any).Linker(); +const a3 = new (getPlugins()).Linker(); +const a4 = new (getPlugins() as any)!.Linker(); +const a5 = new (getPlugins() as any)["Linker"](); +const a6 = new (getPlugins() as any).ns.Linker(); +const a7 = new (getPlugins() as any).Linker; +const a8 = new (getPlugins() as any)`tag`(); +const a9 = new (obj.getPlugins() as any).Linker(); +const a10 = new (getPlugins().ns as any).Linker(); +const a11 = new ((getPlugins() as any) as any).Linker(); +const a12 = new (getPluginsGeneric() as any).Linker(); +const a13 = new (getPluginsFactory() as any).Linker(); +const a14 = new (getPlugins() as any).Linker(new (getPlugins() as any).Options()); + +// Parentheses are not required, or are already present +const b1 = new (getPlugins() as any)(); +const b2 = new (getPlugins()).Linker(); +const b3 = new (new Foo() as any).Linker(); +const b4 = new (new Foo as any).Linker(); +const b5 = new (obj?.getPlugins() as any).Linker(); +const b6 = new (obj[getPlugins()] as any).Linker(); +const b7 = new (obj.ns as any).Linker(); + + +//// [newExpressionCalleeParentheses.js] +"use strict"; +// Parentheses are required when a call is at the start of the member chain of a `new` expression's target +const a1 = new (getPlugins().Linker)(); +const a2 = new (getPlugins().Linker)(); +const a3 = new (getPlugins().Linker)(); +const a4 = new (getPlugins().Linker)(); +const a5 = new (getPlugins()["Linker"])(); +const a6 = new (getPlugins().ns.Linker)(); +const a7 = new (getPlugins().Linker); +const a8 = new (getPlugins() `tag`)(); +const a9 = new (obj.getPlugins().Linker)(); +const a10 = new (getPlugins().ns.Linker)(); +const a11 = new (getPlugins().Linker)(); +const a12 = new (getPluginsGeneric().Linker)(); +const a13 = new (getPluginsFactory().Linker)(); +const a14 = new (getPlugins().Linker)(new (getPlugins().Options)()); +// Parentheses are not required, or are already present +const b1 = new (getPlugins())(); +const b2 = new (getPlugins()).Linker(); +const b3 = new new Foo().Linker(); +const b4 = new (new Foo).Linker(); +const b5 = new (obj?.getPlugins()).Linker(); +const b6 = new obj[getPlugins()].Linker(); +const b7 = new obj.ns.Linker(); diff --git a/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParentheses.symbols b/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParentheses.symbols new file mode 100644 index 0000000000000..0f87cd7ad8d72 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParentheses.symbols @@ -0,0 +1,111 @@ +//// [tests/cases/compiler/newExpressionCalleeParentheses.ts] //// + +=== newExpressionCalleeParentheses.ts === +declare function getPlugins(): any; +>getPlugins : Symbol(getPlugins, Decl(newExpressionCalleeParentheses.ts, 0, 0)) + +declare function getPluginsGeneric(): T; +>getPluginsGeneric : Symbol(getPluginsGeneric, Decl(newExpressionCalleeParentheses.ts, 0, 35)) +>T : Symbol(T, Decl(newExpressionCalleeParentheses.ts, 1, 35)) +>T : Symbol(T, Decl(newExpressionCalleeParentheses.ts, 1, 35)) + +declare function getPluginsFactory(): () => T; +>getPluginsFactory : Symbol(getPluginsFactory, Decl(newExpressionCalleeParentheses.ts, 1, 43)) +>T : Symbol(T, Decl(newExpressionCalleeParentheses.ts, 2, 39)) +>T : Symbol(T, Decl(newExpressionCalleeParentheses.ts, 2, 39)) + +declare const obj: any; +>obj : Symbol(obj, Decl(newExpressionCalleeParentheses.ts, 3, 13)) + +declare class Foo { [key: string]: any } +>Foo : Symbol(Foo, Decl(newExpressionCalleeParentheses.ts, 3, 23)) +>key : Symbol(key, Decl(newExpressionCalleeParentheses.ts, 4, 21)) + +// Parentheses are required when a call is at the start of the member chain of a `new` expression's target +const a1 = new (getPlugins() as any).Linker(); +>a1 : Symbol(a1, Decl(newExpressionCalleeParentheses.ts, 7, 5)) +>getPlugins : Symbol(getPlugins, Decl(newExpressionCalleeParentheses.ts, 0, 0)) + +const a2 = new (getPlugins() satisfies any).Linker(); +>a2 : Symbol(a2, Decl(newExpressionCalleeParentheses.ts, 8, 5)) +>getPlugins : Symbol(getPlugins, Decl(newExpressionCalleeParentheses.ts, 0, 0)) + +const a3 = new (getPlugins()).Linker(); +>a3 : Symbol(a3, Decl(newExpressionCalleeParentheses.ts, 9, 5)) +>getPlugins : Symbol(getPlugins, Decl(newExpressionCalleeParentheses.ts, 0, 0)) + +const a4 = new (getPlugins() as any)!.Linker(); +>a4 : Symbol(a4, Decl(newExpressionCalleeParentheses.ts, 10, 5)) +>getPlugins : Symbol(getPlugins, Decl(newExpressionCalleeParentheses.ts, 0, 0)) + +const a5 = new (getPlugins() as any)["Linker"](); +>a5 : Symbol(a5, Decl(newExpressionCalleeParentheses.ts, 11, 5)) +>getPlugins : Symbol(getPlugins, Decl(newExpressionCalleeParentheses.ts, 0, 0)) + +const a6 = new (getPlugins() as any).ns.Linker(); +>a6 : Symbol(a6, Decl(newExpressionCalleeParentheses.ts, 12, 5)) +>getPlugins : Symbol(getPlugins, Decl(newExpressionCalleeParentheses.ts, 0, 0)) + +const a7 = new (getPlugins() as any).Linker; +>a7 : Symbol(a7, Decl(newExpressionCalleeParentheses.ts, 13, 5)) +>getPlugins : Symbol(getPlugins, Decl(newExpressionCalleeParentheses.ts, 0, 0)) + +const a8 = new (getPlugins() as any)`tag`(); +>a8 : Symbol(a8, Decl(newExpressionCalleeParentheses.ts, 14, 5)) +>getPlugins : Symbol(getPlugins, Decl(newExpressionCalleeParentheses.ts, 0, 0)) + +const a9 = new (obj.getPlugins() as any).Linker(); +>a9 : Symbol(a9, Decl(newExpressionCalleeParentheses.ts, 15, 5)) +>obj : Symbol(obj, Decl(newExpressionCalleeParentheses.ts, 3, 13)) + +const a10 = new (getPlugins().ns as any).Linker(); +>a10 : Symbol(a10, Decl(newExpressionCalleeParentheses.ts, 16, 5)) +>getPlugins : Symbol(getPlugins, Decl(newExpressionCalleeParentheses.ts, 0, 0)) + +const a11 = new ((getPlugins() as any) as any).Linker(); +>a11 : Symbol(a11, Decl(newExpressionCalleeParentheses.ts, 17, 5)) +>getPlugins : Symbol(getPlugins, Decl(newExpressionCalleeParentheses.ts, 0, 0)) + +const a12 = new (getPluginsGeneric() as any).Linker(); +>a12 : Symbol(a12, Decl(newExpressionCalleeParentheses.ts, 18, 5)) +>getPluginsGeneric : Symbol(getPluginsGeneric, Decl(newExpressionCalleeParentheses.ts, 0, 35)) + +const a13 = new (getPluginsFactory() as any).Linker(); +>a13 : Symbol(a13, Decl(newExpressionCalleeParentheses.ts, 19, 5)) +>getPluginsFactory : Symbol(getPluginsFactory, Decl(newExpressionCalleeParentheses.ts, 1, 43)) + +const a14 = new (getPlugins() as any).Linker(new (getPlugins() as any).Options()); +>a14 : Symbol(a14, Decl(newExpressionCalleeParentheses.ts, 20, 5)) +>getPlugins : Symbol(getPlugins, Decl(newExpressionCalleeParentheses.ts, 0, 0)) +>getPlugins : Symbol(getPlugins, Decl(newExpressionCalleeParentheses.ts, 0, 0)) + +// Parentheses are not required, or are already present +const b1 = new (getPlugins() as any)(); +>b1 : Symbol(b1, Decl(newExpressionCalleeParentheses.ts, 23, 5)) +>getPlugins : Symbol(getPlugins, Decl(newExpressionCalleeParentheses.ts, 0, 0)) + +const b2 = new (getPlugins()).Linker(); +>b2 : Symbol(b2, Decl(newExpressionCalleeParentheses.ts, 24, 5)) +>getPlugins : Symbol(getPlugins, Decl(newExpressionCalleeParentheses.ts, 0, 0)) + +const b3 = new (new Foo() as any).Linker(); +>b3 : Symbol(b3, Decl(newExpressionCalleeParentheses.ts, 25, 5)) +>Foo : Symbol(Foo, Decl(newExpressionCalleeParentheses.ts, 3, 23)) + +const b4 = new (new Foo as any).Linker(); +>b4 : Symbol(b4, Decl(newExpressionCalleeParentheses.ts, 26, 5)) +>Foo : Symbol(Foo, Decl(newExpressionCalleeParentheses.ts, 3, 23)) + +const b5 = new (obj?.getPlugins() as any).Linker(); +>b5 : Symbol(b5, Decl(newExpressionCalleeParentheses.ts, 27, 5)) +>obj : Symbol(obj, Decl(newExpressionCalleeParentheses.ts, 3, 13)) + +const b6 = new (obj[getPlugins()] as any).Linker(); +>b6 : Symbol(b6, Decl(newExpressionCalleeParentheses.ts, 28, 5)) +>obj : Symbol(obj, Decl(newExpressionCalleeParentheses.ts, 3, 13)) +>getPlugins : Symbol(getPlugins, Decl(newExpressionCalleeParentheses.ts, 0, 0)) + +const b7 = new (obj.ns as any).Linker(); +>b7 : Symbol(b7, Decl(newExpressionCalleeParentheses.ts, 29, 5)) +>obj : Symbol(obj, Decl(newExpressionCalleeParentheses.ts, 3, 13)) + diff --git a/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParentheses.types b/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParentheses.types new file mode 100644 index 0000000000000..5ca940ec1cdd8 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParentheses.types @@ -0,0 +1,250 @@ +//// [tests/cases/compiler/newExpressionCalleeParentheses.ts] //// + +=== newExpressionCalleeParentheses.ts === +declare function getPlugins(): any; +>getPlugins : () => any + +declare function getPluginsGeneric(): T; +>getPluginsGeneric : () => T + +declare function getPluginsFactory(): () => T; +>getPluginsFactory : () => () => T + +declare const obj: any; +>obj : any + +declare class Foo { [key: string]: any } +>Foo : Foo +>key : string + +// Parentheses are required when a call is at the start of the member chain of a `new` expression's target +const a1 = new (getPlugins() as any).Linker(); +>a1 : any +>new (getPlugins() as any).Linker() : any +>(getPlugins() as any).Linker : any +>(getPlugins() as any) : any +>getPlugins() as any : any +>getPlugins() : any +>getPlugins : () => any +>Linker : any + +const a2 = new (getPlugins() satisfies any).Linker(); +>a2 : any +>new (getPlugins() satisfies any).Linker() : any +>(getPlugins() satisfies any).Linker : any +>(getPlugins() satisfies any) : any +>getPlugins() satisfies any : any +>getPlugins() : any +>getPlugins : () => any +>Linker : any + +const a3 = new (getPlugins()).Linker(); +>a3 : any +>new (getPlugins()).Linker() : any +>(getPlugins()).Linker : any +>(getPlugins()) : any +>getPlugins() : any +>getPlugins() : any +>getPlugins : () => any +>Linker : any + +const a4 = new (getPlugins() as any)!.Linker(); +>a4 : any +>new (getPlugins() as any)!.Linker() : any +>(getPlugins() as any)!.Linker : any +>(getPlugins() as any)! : any +>(getPlugins() as any) : any +>getPlugins() as any : any +>getPlugins() : any +>getPlugins : () => any +>Linker : any + +const a5 = new (getPlugins() as any)["Linker"](); +>a5 : any +>new (getPlugins() as any)["Linker"]() : any +>(getPlugins() as any)["Linker"] : any +>(getPlugins() as any) : any +>getPlugins() as any : any +>getPlugins() : any +>getPlugins : () => any +>"Linker" : "Linker" + +const a6 = new (getPlugins() as any).ns.Linker(); +>a6 : any +>new (getPlugins() as any).ns.Linker() : any +>(getPlugins() as any).ns.Linker : any +>(getPlugins() as any).ns : any +>(getPlugins() as any) : any +>getPlugins() as any : any +>getPlugins() : any +>getPlugins : () => any +>ns : any +>Linker : any + +const a7 = new (getPlugins() as any).Linker; +>a7 : any +>new (getPlugins() as any).Linker : any +>(getPlugins() as any).Linker : any +>(getPlugins() as any) : any +>getPlugins() as any : any +>getPlugins() : any +>getPlugins : () => any +>Linker : any + +const a8 = new (getPlugins() as any)`tag`(); +>a8 : any +>new (getPlugins() as any)`tag`() : any +>(getPlugins() as any)`tag` : any +>(getPlugins() as any) : any +>getPlugins() as any : any +>getPlugins() : any +>getPlugins : () => any +>`tag` : "tag" + +const a9 = new (obj.getPlugins() as any).Linker(); +>a9 : any +>new (obj.getPlugins() as any).Linker() : any +>(obj.getPlugins() as any).Linker : any +>(obj.getPlugins() as any) : any +>obj.getPlugins() as any : any +>obj.getPlugins() : any +>obj.getPlugins : any +>obj : any +>getPlugins : any +>Linker : any + +const a10 = new (getPlugins().ns as any).Linker(); +>a10 : any +>new (getPlugins().ns as any).Linker() : any +>(getPlugins().ns as any).Linker : any +>(getPlugins().ns as any) : any +>getPlugins().ns as any : any +>getPlugins().ns : any +>getPlugins() : any +>getPlugins : () => any +>ns : any +>Linker : any + +const a11 = new ((getPlugins() as any) as any).Linker(); +>a11 : any +>new ((getPlugins() as any) as any).Linker() : any +>((getPlugins() as any) as any).Linker : any +>((getPlugins() as any) as any) : any +>(getPlugins() as any) as any : any +>(getPlugins() as any) : any +>getPlugins() as any : any +>getPlugins() : any +>getPlugins : () => any +>Linker : any + +const a12 = new (getPluginsGeneric() as any).Linker(); +>a12 : any +>new (getPluginsGeneric() as any).Linker() : any +>(getPluginsGeneric() as any).Linker : any +>(getPluginsGeneric() as any) : any +>getPluginsGeneric() as any : any +>getPluginsGeneric() : any +>getPluginsGeneric : () => T +>Linker : any + +const a13 = new (getPluginsFactory() as any).Linker(); +>a13 : any +>new (getPluginsFactory() as any).Linker() : any +>(getPluginsFactory() as any).Linker : any +>(getPluginsFactory() as any) : any +>getPluginsFactory() as any : any +>getPluginsFactory() : () => string +>getPluginsFactory() : () => T +>getPluginsFactory : () => () => T +>Linker : any + +const a14 = new (getPlugins() as any).Linker(new (getPlugins() as any).Options()); +>a14 : any +>new (getPlugins() as any).Linker(new (getPlugins() as any).Options()) : any +>(getPlugins() as any).Linker : any +>(getPlugins() as any) : any +>getPlugins() as any : any +>getPlugins() : any +>getPlugins : () => any +>Linker : any +>new (getPlugins() as any).Options() : any +>(getPlugins() as any).Options : any +>(getPlugins() as any) : any +>getPlugins() as any : any +>getPlugins() : any +>getPlugins : () => any +>Options : any + +// Parentheses are not required, or are already present +const b1 = new (getPlugins() as any)(); +>b1 : any +>new (getPlugins() as any)() : any +>(getPlugins() as any) : any +>getPlugins() as any : any +>getPlugins() : any +>getPlugins : () => any + +const b2 = new (getPlugins()).Linker(); +>b2 : any +>new (getPlugins()).Linker() : any +>(getPlugins()).Linker : any +>(getPlugins()) : any +>getPlugins() : any +>getPlugins : () => any +>Linker : any + +const b3 = new (new Foo() as any).Linker(); +>b3 : any +>new (new Foo() as any).Linker() : any +>(new Foo() as any).Linker : any +>(new Foo() as any) : any +>new Foo() as any : any +>new Foo() : Foo +>Foo : typeof Foo +>Linker : any + +const b4 = new (new Foo as any).Linker(); +>b4 : any +>new (new Foo as any).Linker() : any +>(new Foo as any).Linker : any +>(new Foo as any) : any +>new Foo as any : any +>new Foo : Foo +>Foo : typeof Foo +>Linker : any + +const b5 = new (obj?.getPlugins() as any).Linker(); +>b5 : any +>new (obj?.getPlugins() as any).Linker() : any +>(obj?.getPlugins() as any).Linker : any +>(obj?.getPlugins() as any) : any +>obj?.getPlugins() as any : any +>obj?.getPlugins() : any +>obj?.getPlugins : any +>obj : any +>getPlugins : any +>Linker : any + +const b6 = new (obj[getPlugins()] as any).Linker(); +>b6 : any +>new (obj[getPlugins()] as any).Linker() : any +>(obj[getPlugins()] as any).Linker : any +>(obj[getPlugins()] as any) : any +>obj[getPlugins()] as any : any +>obj[getPlugins()] : any +>obj : any +>getPlugins() : any +>getPlugins : () => any +>Linker : any + +const b7 = new (obj.ns as any).Linker(); +>b7 : any +>new (obj.ns as any).Linker() : any +>(obj.ns as any).Linker : any +>(obj.ns as any) : any +>obj.ns as any : any +>obj.ns : any +>obj : any +>ns : any +>Linker : any + diff --git a/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParenthesesDownlevel.js b/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParenthesesDownlevel.js new file mode 100644 index 0000000000000..34b48ca9357c6 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParenthesesDownlevel.js @@ -0,0 +1,73 @@ +//// [tests/cases/compiler/newExpressionCalleeParenthesesDownlevel.ts] //// + +//// [newExpressionCalleeParenthesesDownlevel.ts] +class Plugins { + static Linker = class {}; +} + +class A { + #plugins = Plugins; + static #staticPlugins = Plugins; + + m() { + new this.#plugins.Linker(); + new this.#plugins["Linker"](); + new A.#staticPlugins.Linker(); + } +} + +class B { + static Plugins = Plugins; +} + +class C extends B { + static async m(key: "Plugins") { + new super[key].Linker(); + } +} + + +//// [newExpressionCalleeParenthesesDownlevel.js] +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +}; +var _a, _A_plugins, _A_staticPlugins; +class Plugins { +} +Plugins.Linker = class { +}; +class A { + constructor() { + _A_plugins.set(this, Plugins); + } + m() { + new (__classPrivateFieldGet(this, _A_plugins, "f").Linker)(); + new (__classPrivateFieldGet(this, _A_plugins, "f")["Linker"])(); + new (__classPrivateFieldGet(_a, _a, "f", _A_staticPlugins).Linker)(); + } +} +_a = A, _A_plugins = new WeakMap(); +_A_staticPlugins = { value: Plugins }; +class B { +} +B.Plugins = Plugins; +class C extends B { + static m(key) { + const _superIndex = name => super[name]; + return __awaiter(this, void 0, void 0, function* () { + new (_superIndex(key).Linker)(); + }); + } +} diff --git a/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParenthesesDownlevel.symbols b/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParenthesesDownlevel.symbols new file mode 100644 index 0000000000000..cacdd236cc75b --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParenthesesDownlevel.symbols @@ -0,0 +1,67 @@ +//// [tests/cases/compiler/newExpressionCalleeParenthesesDownlevel.ts] //// + +=== newExpressionCalleeParenthesesDownlevel.ts === +class Plugins { +>Plugins : Symbol(Plugins, Decl(newExpressionCalleeParenthesesDownlevel.ts, 0, 0)) + + static Linker = class {}; +>Linker : Symbol(Plugins.Linker, Decl(newExpressionCalleeParenthesesDownlevel.ts, 0, 15)) +} + +class A { +>A : Symbol(A, Decl(newExpressionCalleeParenthesesDownlevel.ts, 2, 1)) + + #plugins = Plugins; +>#plugins : Symbol(A.#plugins, Decl(newExpressionCalleeParenthesesDownlevel.ts, 4, 9)) +>Plugins : Symbol(Plugins, Decl(newExpressionCalleeParenthesesDownlevel.ts, 0, 0)) + + static #staticPlugins = Plugins; +>#staticPlugins : Symbol(A.#staticPlugins, Decl(newExpressionCalleeParenthesesDownlevel.ts, 5, 23)) +>Plugins : Symbol(Plugins, Decl(newExpressionCalleeParenthesesDownlevel.ts, 0, 0)) + + m() { +>m : Symbol(A.m, Decl(newExpressionCalleeParenthesesDownlevel.ts, 6, 36)) + + new this.#plugins.Linker(); +>this.#plugins.Linker : Symbol(Plugins.Linker, Decl(newExpressionCalleeParenthesesDownlevel.ts, 0, 15)) +>this.#plugins : Symbol(A.#plugins, Decl(newExpressionCalleeParenthesesDownlevel.ts, 4, 9)) +>this : Symbol(A, Decl(newExpressionCalleeParenthesesDownlevel.ts, 2, 1)) +>Linker : Symbol(Plugins.Linker, Decl(newExpressionCalleeParenthesesDownlevel.ts, 0, 15)) + + new this.#plugins["Linker"](); +>this.#plugins : Symbol(A.#plugins, Decl(newExpressionCalleeParenthesesDownlevel.ts, 4, 9)) +>this : Symbol(A, Decl(newExpressionCalleeParenthesesDownlevel.ts, 2, 1)) +>"Linker" : Symbol(Plugins.Linker, Decl(newExpressionCalleeParenthesesDownlevel.ts, 0, 15)) + + new A.#staticPlugins.Linker(); +>A.#staticPlugins.Linker : Symbol(Plugins.Linker, Decl(newExpressionCalleeParenthesesDownlevel.ts, 0, 15)) +>A.#staticPlugins : Symbol(A.#staticPlugins, Decl(newExpressionCalleeParenthesesDownlevel.ts, 5, 23)) +>A : Symbol(A, Decl(newExpressionCalleeParenthesesDownlevel.ts, 2, 1)) +>Linker : Symbol(Plugins.Linker, Decl(newExpressionCalleeParenthesesDownlevel.ts, 0, 15)) + } +} + +class B { +>B : Symbol(B, Decl(newExpressionCalleeParenthesesDownlevel.ts, 13, 1)) + + static Plugins = Plugins; +>Plugins : Symbol(B.Plugins, Decl(newExpressionCalleeParenthesesDownlevel.ts, 15, 9)) +>Plugins : Symbol(Plugins, Decl(newExpressionCalleeParenthesesDownlevel.ts, 0, 0)) +} + +class C extends B { +>C : Symbol(C, Decl(newExpressionCalleeParenthesesDownlevel.ts, 17, 1)) +>B : Symbol(B, Decl(newExpressionCalleeParenthesesDownlevel.ts, 13, 1)) + + static async m(key: "Plugins") { +>m : Symbol(C.m, Decl(newExpressionCalleeParenthesesDownlevel.ts, 19, 19)) +>key : Symbol(key, Decl(newExpressionCalleeParenthesesDownlevel.ts, 20, 19)) + + new super[key].Linker(); +>super[key].Linker : Symbol(Plugins.Linker, Decl(newExpressionCalleeParenthesesDownlevel.ts, 0, 15)) +>super : Symbol(B, Decl(newExpressionCalleeParenthesesDownlevel.ts, 13, 1)) +>key : Symbol(key, Decl(newExpressionCalleeParenthesesDownlevel.ts, 20, 19)) +>Linker : Symbol(Plugins.Linker, Decl(newExpressionCalleeParenthesesDownlevel.ts, 0, 15)) + } +} + diff --git a/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParenthesesDownlevel.types b/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParenthesesDownlevel.types new file mode 100644 index 0000000000000..eef93be52c05b --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/newExpressionCalleeParenthesesDownlevel.types @@ -0,0 +1,74 @@ +//// [tests/cases/compiler/newExpressionCalleeParenthesesDownlevel.ts] //// + +=== newExpressionCalleeParenthesesDownlevel.ts === +class Plugins { +>Plugins : Plugins + + static Linker = class {}; +>Linker : typeof (Anonymous class) +>class {} : typeof (Anonymous class) +} + +class A { +>A : A + + #plugins = Plugins; +>#plugins : typeof Plugins +>Plugins : typeof Plugins + + static #staticPlugins = Plugins; +>#staticPlugins : typeof Plugins +>Plugins : typeof Plugins + + m() { +>m : () => void + + new this.#plugins.Linker(); +>new this.#plugins.Linker() : (Anonymous class) +>this.#plugins.Linker : typeof (Anonymous class) +>this.#plugins : typeof Plugins +>this : this +>Linker : typeof (Anonymous class) + + new this.#plugins["Linker"](); +>new this.#plugins["Linker"]() : (Anonymous class) +>this.#plugins["Linker"] : typeof (Anonymous class) +>this.#plugins : typeof Plugins +>this : this +>"Linker" : "Linker" + + new A.#staticPlugins.Linker(); +>new A.#staticPlugins.Linker() : (Anonymous class) +>A.#staticPlugins.Linker : typeof (Anonymous class) +>A.#staticPlugins : typeof Plugins +>A : typeof A +>Linker : typeof (Anonymous class) + } +} + +class B { +>B : B + + static Plugins = Plugins; +>Plugins : typeof Plugins +>Plugins : typeof Plugins +} + +class C extends B { +>C : C +>B : B + + static async m(key: "Plugins") { +>m : (key: "Plugins") => Promise +>key : "Plugins" + + new super[key].Linker(); +>new super[key].Linker() : (Anonymous class) +>super[key].Linker : typeof (Anonymous class) +>super[key] : typeof Plugins +>super : typeof B +>key : "Plugins" +>Linker : typeof (Anonymous class) + } +} + diff --git a/tsc/testdata/tests/cases/compiler/newExpressionCalleeParentheses.ts b/tsc/testdata/tests/cases/compiler/newExpressionCalleeParentheses.ts new file mode 100644 index 0000000000000..5442308db36cc --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/newExpressionCalleeParentheses.ts @@ -0,0 +1,32 @@ +// @target: esnext + +declare function getPlugins(): any; +declare function getPluginsGeneric(): T; +declare function getPluginsFactory(): () => T; +declare const obj: any; +declare class Foo { [key: string]: any } + +// Parentheses are required when a call is at the start of the member chain of a `new` expression's target +const a1 = new (getPlugins() as any).Linker(); +const a2 = new (getPlugins() satisfies any).Linker(); +const a3 = new (getPlugins()).Linker(); +const a4 = new (getPlugins() as any)!.Linker(); +const a5 = new (getPlugins() as any)["Linker"](); +const a6 = new (getPlugins() as any).ns.Linker(); +const a7 = new (getPlugins() as any).Linker; +const a8 = new (getPlugins() as any)`tag`(); +const a9 = new (obj.getPlugins() as any).Linker(); +const a10 = new (getPlugins().ns as any).Linker(); +const a11 = new ((getPlugins() as any) as any).Linker(); +const a12 = new (getPluginsGeneric() as any).Linker(); +const a13 = new (getPluginsFactory() as any).Linker(); +const a14 = new (getPlugins() as any).Linker(new (getPlugins() as any).Options()); + +// Parentheses are not required, or are already present +const b1 = new (getPlugins() as any)(); +const b2 = new (getPlugins()).Linker(); +const b3 = new (new Foo() as any).Linker(); +const b4 = new (new Foo as any).Linker(); +const b5 = new (obj?.getPlugins() as any).Linker(); +const b6 = new (obj[getPlugins()] as any).Linker(); +const b7 = new (obj.ns as any).Linker(); diff --git a/tsc/testdata/tests/cases/compiler/newExpressionCalleeParenthesesDownlevel.ts b/tsc/testdata/tests/cases/compiler/newExpressionCalleeParenthesesDownlevel.ts new file mode 100644 index 0000000000000..1659a6b828f60 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/newExpressionCalleeParenthesesDownlevel.ts @@ -0,0 +1,26 @@ +// @target: es2015 + +class Plugins { + static Linker = class {}; +} + +class A { + #plugins = Plugins; + static #staticPlugins = Plugins; + + m() { + new this.#plugins.Linker(); + new this.#plugins["Linker"](); + new A.#staticPlugins.Linker(); + } +} + +class B { + static Plugins = Plugins; +} + +class C extends B { + static async m(key: "Plugins") { + new super[key].Linker(); + } +}