From db752b848471094f6cb20be6110debf462274008 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:33:59 +0000 Subject: [PATCH 1/3] Initial plan From 90069ba4316cb2e1525bbd40b2f40e5c0b8a7549 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:53:29 +0000 Subject: [PATCH 2/3] Fix decorated class methods with Symbol names to emit valid code Add visitPropertyNameOfClassElement function to handle computed property names for decorated class elements. When a class element has decorators and a computed property name that is not a simple inlineable expression, the function: 1. Creates a generated name for the computed property 2. Hoists a variable declaration for that name 3. Updates the computed property to assign the expression to that name This ensures the __decorate call can reference the same value that was used in the class definition. Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- .../tstransforms/legacydecorators.go | 28 ++- .../legacyDecoratorSymbolMethodName.js | 36 ++++ .../legacyDecoratorSymbolMethodName.symbols | 17 ++ .../legacyDecoratorSymbolMethodName.types | 20 ++ .../decoratorsOnComputedProperties.js | 175 ++++++++--------- .../decoratorsOnComputedProperties.js.diff | 177 +++++++++--------- .../legacyDecoratorSymbolMethodName.ts | 12 ++ 7 files changed, 286 insertions(+), 179 deletions(-) create mode 100644 testdata/baselines/reference/compiler/legacyDecoratorSymbolMethodName.js create mode 100644 testdata/baselines/reference/compiler/legacyDecoratorSymbolMethodName.symbols create mode 100644 testdata/baselines/reference/compiler/legacyDecoratorSymbolMethodName.types create mode 100644 testdata/tests/cases/compiler/legacyDecoratorSymbolMethodName.ts diff --git a/internal/transformers/tstransforms/legacydecorators.go b/internal/transformers/tstransforms/legacydecorators.go index 1eab768be6d..afcca40b75a 100644 --- a/internal/transformers/tstransforms/legacydecorators.go +++ b/internal/transformers/tstransforms/legacydecorators.go @@ -165,6 +165,26 @@ func (tx *LegacyDecoratorsTransformer) visitParamerDeclaration(node *ast.Paramet return updated } +/** + * Visits the property name of a class element, for use when emitting property + * initializers. For a computed property on a node with decorators, a temporary + * value is stored for later use. + */ +func (tx *LegacyDecoratorsTransformer) visitPropertyNameOfClassElement(member *ast.Node) *ast.Node { + name := member.Name() + // Computed property names need to be transformed into a hoisted variable when they are used more than once. + // The names are used more than once when the property has a decorator. + if ast.IsComputedPropertyName(name) && ast.HasDecorators(member) { + expression := tx.Visitor().VisitNode(name.AsComputedPropertyName().Expression) + if expression != nil && !transformers.IsSimpleInlineableExpression(expression) { + generatedName := tx.Factory().NewGeneratedNameForNode(name) + tx.EmitContext().AddVariableDeclaration(generatedName) + return tx.Factory().UpdateComputedPropertyName(name.AsComputedPropertyName(), tx.Factory().NewAssignmentExpression(generatedName, expression)) + } + } + return tx.Visitor().VisitNode(name) +} + func (tx *LegacyDecoratorsTransformer) visitPropertyDeclaration(node *ast.PropertyDeclaration) *ast.Node { if (node.Flags & ast.NodeFlagsAmbient) != 0 { return nil @@ -177,7 +197,7 @@ func (tx *LegacyDecoratorsTransformer) visitPropertyDeclaration(node *ast.Proper tx.Factory().UpdatePropertyDeclaration( node, tx.Visitor().VisitModifiers(node.Modifiers()), - tx.Visitor().VisitNode(node.Name()), + tx.visitPropertyNameOfClassElement(node.AsNode()), nil, nil, tx.Visitor().VisitNode(node.Initializer), @@ -191,7 +211,7 @@ func (tx *LegacyDecoratorsTransformer) visitGetAccessorDeclaration(node *ast.Get tx.Factory().UpdateGetAccessorDeclaration( node, tx.Visitor().VisitModifiers(node.Modifiers()), - tx.Visitor().VisitNode(node.Name()), + tx.visitPropertyNameOfClassElement(node.AsNode()), nil, tx.Visitor().VisitNodes(node.Parameters), nil, @@ -207,7 +227,7 @@ func (tx *LegacyDecoratorsTransformer) visitSetAccessorDeclaration(node *ast.Set tx.Factory().UpdateSetAccessorDeclaration( node, tx.Visitor().VisitModifiers(node.Modifiers()), - tx.Visitor().VisitNode(node.Name()), + tx.visitPropertyNameOfClassElement(node.AsNode()), nil, tx.Visitor().VisitNodes(node.Parameters), nil, @@ -224,7 +244,7 @@ func (tx *LegacyDecoratorsTransformer) visitMethodDeclaration(node *ast.MethodDe node, tx.Visitor().VisitModifiers(node.Modifiers()), node.AsteriskToken, - tx.Visitor().VisitNode(node.Name()), + tx.visitPropertyNameOfClassElement(node.AsNode()), nil, nil, tx.Visitor().VisitNodes(node.Parameters), diff --git a/testdata/baselines/reference/compiler/legacyDecoratorSymbolMethodName.js b/testdata/baselines/reference/compiler/legacyDecoratorSymbolMethodName.js new file mode 100644 index 00000000000..1d4a5d9451c --- /dev/null +++ b/testdata/baselines/reference/compiler/legacyDecoratorSymbolMethodName.js @@ -0,0 +1,36 @@ +//// [tests/cases/compiler/legacyDecoratorSymbolMethodName.ts] //// + +//// [legacyDecoratorSymbolMethodName.ts] +export class A { + @(fakeDecorator as any) + [Symbol()]() {} +} + +function fakeDecorator() {} + + +//// [legacyDecoratorSymbolMethodName.js] +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var _a; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.A = void 0; +class A { + [_a = Symbol()]() { } +} +exports.A = A; +__decorate([ + fakeDecorator, + __metadata("design:type", Function), + __metadata("design:paramtypes", []), + __metadata("design:returntype", void 0) +], A.prototype, _a, null); +function fakeDecorator() { } diff --git a/testdata/baselines/reference/compiler/legacyDecoratorSymbolMethodName.symbols b/testdata/baselines/reference/compiler/legacyDecoratorSymbolMethodName.symbols new file mode 100644 index 00000000000..cb6656c6b7d --- /dev/null +++ b/testdata/baselines/reference/compiler/legacyDecoratorSymbolMethodName.symbols @@ -0,0 +1,17 @@ +//// [tests/cases/compiler/legacyDecoratorSymbolMethodName.ts] //// + +=== legacyDecoratorSymbolMethodName.ts === +export class A { +>A : Symbol(A, Decl(legacyDecoratorSymbolMethodName.ts, 0, 0)) + + @(fakeDecorator as any) +>fakeDecorator : Symbol(fakeDecorator, Decl(legacyDecoratorSymbolMethodName.ts, 3, 1)) + + [Symbol()]() {} +>[Symbol()] : Symbol(A[Symbol()], Decl(legacyDecoratorSymbolMethodName.ts, 0, 16)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --)) +} + +function fakeDecorator() {} +>fakeDecorator : Symbol(fakeDecorator, Decl(legacyDecoratorSymbolMethodName.ts, 3, 1)) + diff --git a/testdata/baselines/reference/compiler/legacyDecoratorSymbolMethodName.types b/testdata/baselines/reference/compiler/legacyDecoratorSymbolMethodName.types new file mode 100644 index 00000000000..3003fc243d4 --- /dev/null +++ b/testdata/baselines/reference/compiler/legacyDecoratorSymbolMethodName.types @@ -0,0 +1,20 @@ +//// [tests/cases/compiler/legacyDecoratorSymbolMethodName.ts] //// + +=== legacyDecoratorSymbolMethodName.ts === +export class A { +>A : A + + @(fakeDecorator as any) +>(fakeDecorator as any) : any +>fakeDecorator as any : any +>fakeDecorator : () => void + + [Symbol()]() {} +>[Symbol()] : () => void +>Symbol() : symbol +>Symbol : SymbolConstructor +} + +function fakeDecorator() {} +>fakeDecorator : () => void + diff --git a/testdata/baselines/reference/submodule/compiler/decoratorsOnComputedProperties.js b/testdata/baselines/reference/submodule/compiler/decoratorsOnComputedProperties.js index c4775832cbf..4be798062d8 100644 --- a/testdata/baselines/reference/submodule/compiler/decoratorsOnComputedProperties.js +++ b/testdata/baselines/reference/submodule/compiler/decoratorsOnComputedProperties.js @@ -198,6 +198,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; +var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37; function x(o, k) { } let i = 0; function foo() { return ++i + ""; } @@ -206,19 +207,19 @@ const fieldNameB = "fieldName2"; const fieldNameC = "fieldName3"; class A { ["property"]; - [Symbol.toStringTag]; + [_a = Symbol.toStringTag]; ["property2"] = 2; - [Symbol.iterator] = null; + [_b = Symbol.iterator] = null; ["property3"]; [Symbol.isConcatSpreadable]; ["property4"] = 2; [Symbol.match] = null; [foo()]; - [foo()]; - [foo()] = null; + [_c = foo()]; + [_d = foo()] = null; [fieldNameA]; - [fieldNameB]; - [fieldNameC] = null; + [_e = fieldNameB]; + [_f = fieldNameC] = null; } __decorate([ x @@ -246,35 +247,35 @@ __decorate([ ], A.prototype, _f, void 0); void class B { ["property"]; - [Symbol.toStringTag]; + [_g = Symbol.toStringTag]; ["property2"] = 2; - [Symbol.iterator] = null; + [_h = Symbol.iterator] = null; ["property3"]; [Symbol.isConcatSpreadable]; ["property4"] = 2; [Symbol.match] = null; [foo()]; - [foo()]; - [foo()] = null; + [_j = foo()]; + [_k = foo()] = null; [fieldNameA]; - [fieldNameB]; - [fieldNameC] = null; + [_l = fieldNameB]; + [_m = fieldNameC] = null; }; class C { ["property"]; - [Symbol.toStringTag]; + [_o = Symbol.toStringTag]; ["property2"] = 2; - [Symbol.iterator] = null; + [_p = Symbol.iterator] = null; ["property3"]; [Symbol.isConcatSpreadable]; ["property4"] = 2; [Symbol.match] = null; [foo()]; - [foo()]; - [foo()] = null; + [_q = foo()]; + [_r = foo()] = null; [fieldNameA]; - [fieldNameB]; - [fieldNameC] = null; + [_s = fieldNameB]; + [_t = fieldNameC] = null; ["some" + "method"]() { } } __decorate([ @@ -282,220 +283,220 @@ __decorate([ ], C.prototype, "property", void 0); __decorate([ x -], C.prototype, _g, void 0); +], C.prototype, _o, void 0); __decorate([ x ], C.prototype, "property2", void 0); __decorate([ x -], C.prototype, _h, void 0); +], C.prototype, _p, void 0); __decorate([ x -], C.prototype, _j, void 0); +], C.prototype, _q, void 0); __decorate([ x -], C.prototype, _k, void 0); +], C.prototype, _r, void 0); __decorate([ x -], C.prototype, _l, void 0); +], C.prototype, _s, void 0); __decorate([ x -], C.prototype, _m, void 0); +], C.prototype, _t, void 0); void class D { ["property"]; - [Symbol.toStringTag]; + [_u = Symbol.toStringTag]; ["property2"] = 2; - [Symbol.iterator] = null; + [_v = Symbol.iterator] = null; ["property3"]; [Symbol.isConcatSpreadable]; ["property4"] = 2; [Symbol.match] = null; [foo()]; - [foo()]; - [foo()] = null; + [_w = foo()]; + [_x = foo()] = null; [fieldNameA]; - [fieldNameB]; - [fieldNameC] = null; + [_y = fieldNameB]; + [_z = fieldNameC] = null; ["some" + "method"]() { } }; class E { ["property"]; - [Symbol.toStringTag]; + [_0 = Symbol.toStringTag]; ["property2"] = 2; - [Symbol.iterator] = null; + [_1 = Symbol.iterator] = null; ["property3"]; [Symbol.isConcatSpreadable]; ["property4"] = 2; [Symbol.match] = null; [foo()]; - [foo()]; - [foo()] = null; + [_2 = foo()]; + [_3 = foo()] = null; ["some" + "method"]() { } [fieldNameA]; - [fieldNameB]; - [fieldNameC] = null; + [_4 = fieldNameB]; + [_5 = fieldNameC] = null; } __decorate([ x ], E.prototype, "property", void 0); __decorate([ x -], E.prototype, _o, void 0); +], E.prototype, _0, void 0); __decorate([ x ], E.prototype, "property2", void 0); __decorate([ x -], E.prototype, _p, void 0); +], E.prototype, _1, void 0); __decorate([ x -], E.prototype, _q, void 0); +], E.prototype, _2, void 0); __decorate([ x -], E.prototype, _r, void 0); +], E.prototype, _3, void 0); __decorate([ x -], E.prototype, _s, void 0); +], E.prototype, _4, void 0); __decorate([ x -], E.prototype, _t, void 0); +], E.prototype, _5, void 0); void class F { ["property"]; - [Symbol.toStringTag]; + [_6 = Symbol.toStringTag]; ["property2"] = 2; - [Symbol.iterator] = null; + [_7 = Symbol.iterator] = null; ["property3"]; [Symbol.isConcatSpreadable]; ["property4"] = 2; [Symbol.match] = null; [foo()]; - [foo()]; - [foo()] = null; + [_8 = foo()]; + [_9 = foo()] = null; ["some" + "method"]() { } [fieldNameA]; - [fieldNameB]; - [fieldNameC] = null; + [_10 = fieldNameB]; + [_11 = fieldNameC] = null; }; class G { ["property"]; - [Symbol.toStringTag]; + [_12 = Symbol.toStringTag]; ["property2"] = 2; - [Symbol.iterator] = null; + [_13 = Symbol.iterator] = null; ["property3"]; [Symbol.isConcatSpreadable]; ["property4"] = 2; [Symbol.match] = null; [foo()]; - [foo()]; - [foo()] = null; + [_14 = foo()]; + [_15 = foo()] = null; ["some" + "method"]() { } [fieldNameA]; - [fieldNameB]; + [_16 = fieldNameB]; ["some" + "method2"]() { } - [fieldNameC] = null; + [_17 = fieldNameC] = null; } __decorate([ x ], G.prototype, "property", void 0); __decorate([ x -], G.prototype, _u, void 0); +], G.prototype, _12, void 0); __decorate([ x ], G.prototype, "property2", void 0); __decorate([ x -], G.prototype, _v, void 0); +], G.prototype, _13, void 0); __decorate([ x -], G.prototype, _w, void 0); +], G.prototype, _14, void 0); __decorate([ x -], G.prototype, _x, void 0); +], G.prototype, _15, void 0); __decorate([ x -], G.prototype, _y, void 0); +], G.prototype, _16, void 0); __decorate([ x -], G.prototype, _z, void 0); +], G.prototype, _17, void 0); void class H { ["property"]; - [Symbol.toStringTag]; + [_18 = Symbol.toStringTag]; ["property2"] = 2; - [Symbol.iterator] = null; + [_19 = Symbol.iterator] = null; ["property3"]; [Symbol.isConcatSpreadable]; ["property4"] = 2; [Symbol.match] = null; [foo()]; - [foo()]; - [foo()] = null; + [_20 = foo()]; + [_21 = foo()] = null; ["some" + "method"]() { } [fieldNameA]; - [fieldNameB]; + [_22 = fieldNameB]; ["some" + "method2"]() { } - [fieldNameC] = null; + [_23 = fieldNameC] = null; }; class I { ["property"]; - [Symbol.toStringTag]; + [_24 = Symbol.toStringTag]; ["property2"] = 2; - [Symbol.iterator] = null; + [_25 = Symbol.iterator] = null; ["property3"]; [Symbol.isConcatSpreadable]; ["property4"] = 2; [Symbol.match] = null; [foo()]; - [foo()]; - [foo()] = null; - ["some" + "method"]() { } + [_26 = foo()]; + [_27 = foo()] = null; + [_28 = "some" + "method"]() { } [fieldNameA]; - [fieldNameB]; + [_29 = fieldNameB]; ["some" + "method2"]() { } - [fieldNameC] = null; + [_30 = fieldNameC] = null; } __decorate([ x ], I.prototype, "property", void 0); __decorate([ x -], I.prototype, _0, void 0); +], I.prototype, _24, void 0); __decorate([ x ], I.prototype, "property2", void 0); __decorate([ x -], I.prototype, _1, void 0); +], I.prototype, _25, void 0); __decorate([ x -], I.prototype, _2, void 0); +], I.prototype, _26, void 0); __decorate([ x -], I.prototype, _3, void 0); +], I.prototype, _27, void 0); __decorate([ x -], I.prototype, _4, null); +], I.prototype, _28, null); __decorate([ x -], I.prototype, _5, void 0); +], I.prototype, _29, void 0); __decorate([ x -], I.prototype, _6, void 0); +], I.prototype, _30, void 0); void class J { ["property"]; - [Symbol.toStringTag]; + [_31 = Symbol.toStringTag]; ["property2"] = 2; - [Symbol.iterator] = null; + [_32 = Symbol.iterator] = null; ["property3"]; [Symbol.isConcatSpreadable]; ["property4"] = 2; [Symbol.match] = null; [foo()]; - [foo()]; - [foo()] = null; - ["some" + "method"]() { } + [_33 = foo()]; + [_34 = foo()] = null; + [_35 = "some" + "method"]() { } [fieldNameA]; - [fieldNameB]; + [_36 = fieldNameB]; ["some" + "method2"]() { } - [fieldNameC] = null; + [_37 = fieldNameC] = null; }; diff --git a/testdata/baselines/reference/submodule/compiler/decoratorsOnComputedProperties.js.diff b/testdata/baselines/reference/submodule/compiler/decoratorsOnComputedProperties.js.diff index 0852db0cc3a..bdf5998e15e 100644 --- a/testdata/baselines/reference/submodule/compiler/decoratorsOnComputedProperties.js.diff +++ b/testdata/baselines/reference/submodule/compiler/decoratorsOnComputedProperties.js.diff @@ -6,10 +6,11 @@ }; -var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p; -var _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51; ++var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37; function x(o, k) { } let i = 0; function foo() { return ++i + ""; } -@@= skipped -9, +7 lines =@@ +@@= skipped -9, +8 lines =@@ const fieldNameB = "fieldName2"; const fieldNameC = "fieldName3"; class A { @@ -22,19 +23,19 @@ - this[_v] = null; - } + ["property"]; -+ [Symbol.toStringTag]; ++ [_a = Symbol.toStringTag]; + ["property2"] = 2; -+ [Symbol.iterator] = null; ++ [_b = Symbol.iterator] = null; + ["property3"]; + [Symbol.isConcatSpreadable]; + ["property4"] = 2; + [Symbol.match] = null; + [foo()]; -+ [foo()]; -+ [foo()] = null; ++ [_c = foo()]; ++ [_d = foo()] = null; + [fieldNameA]; -+ [fieldNameB]; -+ [fieldNameC] = null; ++ [_e = fieldNameB]; ++ [_f = fieldNameC] = null; } -_q = Symbol.toStringTag, _r = Symbol.iterator, Symbol.isConcatSpreadable, _a = Symbol.match, foo(), _s = foo(), _t = foo(), _u = fieldNameB, _v = fieldNameC; __decorate([ @@ -97,19 +98,19 @@ +], A.prototype, _f, void 0); +void class B { + ["property"]; -+ [Symbol.toStringTag]; ++ [_g = Symbol.toStringTag]; + ["property2"] = 2; -+ [Symbol.iterator] = null; ++ [_h = Symbol.iterator] = null; + ["property3"]; + [Symbol.isConcatSpreadable]; + ["property4"] = 2; + [Symbol.match] = null; + [foo()]; -+ [foo()]; -+ [foo()] = null; ++ [_j = foo()]; ++ [_k = foo()] = null; + [fieldNameA]; -+ [fieldNameB]; -+ [fieldNameC] = null; ++ [_l = fieldNameB]; ++ [_m = fieldNameC] = null; +}; class C { - constructor() { @@ -122,19 +123,19 @@ - } - [(_2 = Symbol.toStringTag, _3 = Symbol.iterator, Symbol.isConcatSpreadable, _d = Symbol.match, foo(), _4 = foo(), _5 = foo(), _6 = fieldNameB, _7 = fieldNameC, "some" + "method")]() { } + ["property"]; -+ [Symbol.toStringTag]; ++ [_o = Symbol.toStringTag]; + ["property2"] = 2; -+ [Symbol.iterator] = null; ++ [_p = Symbol.iterator] = null; + ["property3"]; + [Symbol.isConcatSpreadable]; + ["property4"] = 2; + [Symbol.match] = null; + [foo()]; -+ [foo()]; -+ [foo()] = null; ++ [_q = foo()]; ++ [_r = foo()] = null; + [fieldNameA]; -+ [fieldNameB]; -+ [fieldNameC] = null; ++ [_s = fieldNameB]; ++ [_t = fieldNameC] = null; + ["some" + "method"]() { } } __decorate([ @@ -143,7 +144,7 @@ __decorate([ x -], C.prototype, _2, void 0); -+], C.prototype, _g, void 0); ++], C.prototype, _o, void 0); __decorate([ x ], C.prototype, "property2", void 0); @@ -162,19 +163,19 @@ -__decorate([ - x -], C.prototype, _7, void 0); -+], C.prototype, _h, void 0); ++], C.prototype, _p, void 0); +__decorate([ + x -+], C.prototype, _j, void 0); ++], C.prototype, _q, void 0); +__decorate([ + x -+], C.prototype, _k, void 0); ++], C.prototype, _r, void 0); +__decorate([ + x -+], C.prototype, _l, void 0); ++], C.prototype, _s, void 0); +__decorate([ + x -+], C.prototype, _m, void 0); ++], C.prototype, _t, void 0); void class D { - constructor() { - this["property2"] = 2; @@ -186,19 +187,19 @@ - } - [(_8 = Symbol.toStringTag, _9 = Symbol.iterator, Symbol.isConcatSpreadable, _e = Symbol.match, foo(), _10 = foo(), _11 = foo(), _12 = fieldNameB, _13 = fieldNameC, "some" + "method")]() { } + ["property"]; -+ [Symbol.toStringTag]; ++ [_u = Symbol.toStringTag]; + ["property2"] = 2; -+ [Symbol.iterator] = null; ++ [_v = Symbol.iterator] = null; + ["property3"]; + [Symbol.isConcatSpreadable]; + ["property4"] = 2; + [Symbol.match] = null; + [foo()]; -+ [foo()]; -+ [foo()] = null; ++ [_w = foo()]; ++ [_x = foo()] = null; + [fieldNameA]; -+ [fieldNameB]; -+ [fieldNameC] = null; ++ [_y = fieldNameB]; ++ [_z = fieldNameC] = null; + ["some" + "method"]() { } }; class E { @@ -212,20 +213,20 @@ - } - [(_14 = Symbol.toStringTag, _15 = Symbol.iterator, Symbol.isConcatSpreadable, _f = Symbol.match, foo(), _16 = foo(), _17 = foo(), "some" + "method")]() { } + ["property"]; -+ [Symbol.toStringTag]; ++ [_0 = Symbol.toStringTag]; + ["property2"] = 2; -+ [Symbol.iterator] = null; ++ [_1 = Symbol.iterator] = null; + ["property3"]; + [Symbol.isConcatSpreadable]; + ["property4"] = 2; + [Symbol.match] = null; + [foo()]; -+ [foo()]; -+ [foo()] = null; ++ [_2 = foo()]; ++ [_3 = foo()] = null; + ["some" + "method"]() { } + [fieldNameA]; -+ [fieldNameB]; -+ [fieldNameC] = null; ++ [_4 = fieldNameB]; ++ [_5 = fieldNameC] = null; } -_18 = fieldNameB, _19 = fieldNameC; __decorate([ @@ -234,7 +235,7 @@ __decorate([ x -], E.prototype, _14, void 0); -+], E.prototype, _o, void 0); ++], E.prototype, _0, void 0); __decorate([ x ], E.prototype, "property2", void 0); @@ -267,35 +268,35 @@ - _24 = fieldNameB, - _25 = fieldNameC, - _h); -+], E.prototype, _p, void 0); ++], E.prototype, _1, void 0); +__decorate([ + x -+], E.prototype, _q, void 0); ++], E.prototype, _2, void 0); +__decorate([ + x -+], E.prototype, _r, void 0); ++], E.prototype, _3, void 0); +__decorate([ + x -+], E.prototype, _s, void 0); ++], E.prototype, _4, void 0); +__decorate([ + x -+], E.prototype, _t, void 0); ++], E.prototype, _5, void 0); +void class F { + ["property"]; -+ [Symbol.toStringTag]; ++ [_6 = Symbol.toStringTag]; + ["property2"] = 2; -+ [Symbol.iterator] = null; ++ [_7 = Symbol.iterator] = null; + ["property3"]; + [Symbol.isConcatSpreadable]; + ["property4"] = 2; + [Symbol.match] = null; + [foo()]; -+ [foo()]; -+ [foo()] = null; ++ [_8 = foo()]; ++ [_9 = foo()] = null; + ["some" + "method"]() { } + [fieldNameA]; -+ [fieldNameB]; -+ [fieldNameC] = null; ++ [_10 = fieldNameB]; ++ [_11 = fieldNameC] = null; +}; class G { - constructor() { @@ -309,21 +310,21 @@ - [(_26 = Symbol.toStringTag, _27 = Symbol.iterator, Symbol.isConcatSpreadable, _j = Symbol.match, foo(), _28 = foo(), _29 = foo(), "some" + "method")]() { } - [(_30 = fieldNameB, "some" + "method2")]() { } + ["property"]; -+ [Symbol.toStringTag]; ++ [_12 = Symbol.toStringTag]; + ["property2"] = 2; -+ [Symbol.iterator] = null; ++ [_13 = Symbol.iterator] = null; + ["property3"]; + [Symbol.isConcatSpreadable]; + ["property4"] = 2; + [Symbol.match] = null; + [foo()]; -+ [foo()]; -+ [foo()] = null; ++ [_14 = foo()]; ++ [_15 = foo()] = null; + ["some" + "method"]() { } + [fieldNameA]; -+ [fieldNameB]; ++ [_16 = fieldNameB]; + ["some" + "method2"]() { } -+ [fieldNameC] = null; ++ [_17 = fieldNameC] = null; } -_31 = fieldNameC; __decorate([ @@ -332,7 +333,7 @@ __decorate([ x -], G.prototype, _26, void 0); -+], G.prototype, _u, void 0); ++], G.prototype, _12, void 0); __decorate([ x ], G.prototype, "property2", void 0); @@ -365,36 +366,36 @@ - }, - _37 = fieldNameC, - _l); -+], G.prototype, _v, void 0); ++], G.prototype, _13, void 0); +__decorate([ + x -+], G.prototype, _w, void 0); ++], G.prototype, _14, void 0); +__decorate([ + x -+], G.prototype, _x, void 0); ++], G.prototype, _15, void 0); +__decorate([ + x -+], G.prototype, _y, void 0); ++], G.prototype, _16, void 0); +__decorate([ + x -+], G.prototype, _z, void 0); ++], G.prototype, _17, void 0); +void class H { + ["property"]; -+ [Symbol.toStringTag]; ++ [_18 = Symbol.toStringTag]; + ["property2"] = 2; -+ [Symbol.iterator] = null; ++ [_19 = Symbol.iterator] = null; + ["property3"]; + [Symbol.isConcatSpreadable]; + ["property4"] = 2; + [Symbol.match] = null; + [foo()]; -+ [foo()]; -+ [foo()] = null; ++ [_20 = foo()]; ++ [_21 = foo()] = null; + ["some" + "method"]() { } + [fieldNameA]; -+ [fieldNameB]; ++ [_22 = fieldNameB]; + ["some" + "method2"]() { } -+ [fieldNameC] = null; ++ [_23 = fieldNameC] = null; +}; class I { - constructor() { @@ -408,21 +409,21 @@ - [(_38 = Symbol.toStringTag, _39 = Symbol.iterator, Symbol.isConcatSpreadable, _m = Symbol.match, foo(), _40 = foo(), _41 = foo(), _42 = "some" + "method")]() { } - [(_43 = fieldNameB, "some" + "method2")]() { } + ["property"]; -+ [Symbol.toStringTag]; ++ [_24 = Symbol.toStringTag]; + ["property2"] = 2; -+ [Symbol.iterator] = null; ++ [_25 = Symbol.iterator] = null; + ["property3"]; + [Symbol.isConcatSpreadable]; + ["property4"] = 2; + [Symbol.match] = null; + [foo()]; -+ [foo()]; -+ [foo()] = null; -+ ["some" + "method"]() { } ++ [_26 = foo()]; ++ [_27 = foo()] = null; ++ [_28 = "some" + "method"]() { } + [fieldNameA]; -+ [fieldNameB]; ++ [_29 = fieldNameB]; + ["some" + "method2"]() { } -+ [fieldNameC] = null; ++ [_30 = fieldNameC] = null; } -_44 = fieldNameC; __decorate([ @@ -431,7 +432,7 @@ __decorate([ x -], I.prototype, _38, void 0); -+], I.prototype, _0, void 0); ++], I.prototype, _24, void 0); __decorate([ x ], I.prototype, "property2", void 0); @@ -467,37 +468,37 @@ - }, - _51 = fieldNameC, - _p); -+], I.prototype, _1, void 0); ++], I.prototype, _25, void 0); +__decorate([ + x -+], I.prototype, _2, void 0); ++], I.prototype, _26, void 0); +__decorate([ + x -+], I.prototype, _3, void 0); ++], I.prototype, _27, void 0); +__decorate([ + x -+], I.prototype, _4, null); ++], I.prototype, _28, null); +__decorate([ + x -+], I.prototype, _5, void 0); ++], I.prototype, _29, void 0); +__decorate([ + x -+], I.prototype, _6, void 0); ++], I.prototype, _30, void 0); +void class J { + ["property"]; -+ [Symbol.toStringTag]; ++ [_31 = Symbol.toStringTag]; + ["property2"] = 2; -+ [Symbol.iterator] = null; ++ [_32 = Symbol.iterator] = null; + ["property3"]; + [Symbol.isConcatSpreadable]; + ["property4"] = 2; + [Symbol.match] = null; + [foo()]; -+ [foo()]; -+ [foo()] = null; -+ ["some" + "method"]() { } ++ [_33 = foo()]; ++ [_34 = foo()] = null; ++ [_35 = "some" + "method"]() { } + [fieldNameA]; -+ [fieldNameB]; ++ [_36 = fieldNameB]; + ["some" + "method2"]() { } -+ [fieldNameC] = null; ++ [_37 = fieldNameC] = null; +}; \ No newline at end of file diff --git a/testdata/tests/cases/compiler/legacyDecoratorSymbolMethodName.ts b/testdata/tests/cases/compiler/legacyDecoratorSymbolMethodName.ts new file mode 100644 index 00000000000..13d7953c0db --- /dev/null +++ b/testdata/tests/cases/compiler/legacyDecoratorSymbolMethodName.ts @@ -0,0 +1,12 @@ +// @target: es2023 +// @module: nodenext +// @strict: true +// @experimentalDecorators: true +// @emitDecoratorMetadata: true + +export class A { + @(fakeDecorator as any) + [Symbol()]() {} +} + +function fakeDecorator() {} From 32a41e6d0d87137154cb7d7b60da35fbe16fa37e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:58:33 +0000 Subject: [PATCH 3/3] Address code review: improve comment clarity Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- .../transformers/tstransforms/legacydecorators.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/internal/transformers/tstransforms/legacydecorators.go b/internal/transformers/tstransforms/legacydecorators.go index afcca40b75a..9932542f24b 100644 --- a/internal/transformers/tstransforms/legacydecorators.go +++ b/internal/transformers/tstransforms/legacydecorators.go @@ -165,15 +165,12 @@ func (tx *LegacyDecoratorsTransformer) visitParamerDeclaration(node *ast.Paramet return updated } -/** - * Visits the property name of a class element, for use when emitting property - * initializers. For a computed property on a node with decorators, a temporary - * value is stored for later use. - */ +// visitPropertyNameOfClassElement visits the property name of a class element. +// Computed property names with decorators need hoisting because the name expression +// must be evaluated once and referenced in both the property definition and the +// decorator call. func (tx *LegacyDecoratorsTransformer) visitPropertyNameOfClassElement(member *ast.Node) *ast.Node { name := member.Name() - // Computed property names need to be transformed into a hoisted variable when they are used more than once. - // The names are used more than once when the property has a decorator. if ast.IsComputedPropertyName(name) && ast.HasDecorators(member) { expression := tx.Visitor().VisitNode(name.AsComputedPropertyName().Expression) if expression != nil && !transformers.IsSimpleInlineableExpression(expression) {