From f2b92d43c6ff8297297037aab75a51d2129f8089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 23 Aug 2026 09:55:27 +0200 Subject: [PATCH 1/4] Fix crash on decorated anonymous class declaration --- .../tstransforms/runtimesyntax.go | 2 +- ...ssDeclarationWithClassDecorator.errors.txt | 12 ++++ ...ymousClassDeclarationWithClassDecorator.js | 63 +++++++++++++++++++ ...ClassDeclarationWithClassDecorator.symbols | 12 ++++ ...usClassDeclarationWithClassDecorator.types | 12 ++++ ...ymousClassDeclarationWithClassDecorator.ts | 7 +++ 6 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.js create mode 100644 tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.types create mode 100644 tsc/testdata/tests/cases/compiler/anonymousClassDeclarationWithClassDecorator.ts diff --git a/tsc/internal/transformers/tstransforms/runtimesyntax.go b/tsc/internal/transformers/tstransforms/runtimesyntax.go index 35c29262606b0..7dc491ce46a8a 100644 --- a/tsc/internal/transformers/tstransforms/runtimesyntax.go +++ b/tsc/internal/transformers/tstransforms/runtimesyntax.go @@ -676,7 +676,7 @@ func (tx *RuntimeSyntaxTransformer) visitClassDeclaration(node *ast.ClassDeclara } name := tx.Visitor().VisitNode(node.Name()) - if name == nil && (exported || ast.ChildIsDecorated(tx.compilerOptions.ExperimentalDecorators.IsTrue(), node.AsNode(), nil)) { + if name == nil && (exported || ast.NodeOrChildIsDecorated(tx.compilerOptions.ExperimentalDecorators.IsTrue(), node.AsNode(), nil, nil)) { name = tx.Factory().NewGeneratedNameForNode(node.AsNode()) } heritageClauses := tx.Visitor().VisitNodes(node.HeritageClauses) diff --git a/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.errors.txt b/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.errors.txt new file mode 100644 index 0000000000000..ee8031adaaa2b --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.errors.txt @@ -0,0 +1,12 @@ +anonymousClassDeclarationWithClassDecorator.ts(3,1): error TS1211: A class declaration without the 'default' modifier must have a name. + + +==== anonymousClassDeclarationWithClassDecorator.ts (1 errors) ==== + declare var dec: any; + + @dec + ~ +!!! error TS1211: A class declaration without the 'default' modifier must have a name. + class { + } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.js b/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.js new file mode 100644 index 0000000000000..380f11c60d845 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.js @@ -0,0 +1,63 @@ +//// [tests/cases/compiler/anonymousClassDeclarationWithClassDecorator.ts] //// + +//// [anonymousClassDeclarationWithClassDecorator.ts] +declare var dec: any; + +@dec +class { +} + + +//// [anonymousClassDeclarationWithClassDecorator.js] +"use strict"; +var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { + function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } + var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; + var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; + var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); + var _, done = false; + for (var i = decorators.length - 1; i >= 0; i--) { + var context = {}; + for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; + for (var p in contextIn.access) context.access[p] = contextIn.access[p]; + context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; + var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); + if (kind === "accessor") { + if (result === void 0) continue; + if (result === null || typeof result !== "object") throw new TypeError("Object expected"); + if (_ = accept(result.get)) descriptor.get = _; + if (_ = accept(result.set)) descriptor.set = _; + if (_ = accept(result.init)) initializers.unshift(_); + } + else if (_ = accept(result)) { + if (kind === "field") initializers.unshift(_); + else descriptor[key] = _; + } + } + if (target) Object.defineProperty(target, contextIn.name, descriptor); + done = true; +}; +var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { + var useValue = arguments.length > 2; + for (var i = 0; i < initializers.length; i++) { + value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); + } + return useValue ? value : void 0; +}; +let default_1 = (() => { + let _classDecorators = [dec]; + let _classDescriptor; + let _classExtraInitializers = []; + let _classThis; + var default_1 = class { + static { _classThis = this; } + static { + const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; + __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); + default_1 = _classThis = _classDescriptor.value; + if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); + __runInitializers(_classThis, _classExtraInitializers); + } + }; + return default_1 = _classThis; +})(); diff --git a/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.symbols b/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.symbols new file mode 100644 index 0000000000000..f5afbe551f012 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.symbols @@ -0,0 +1,12 @@ +//// [tests/cases/compiler/anonymousClassDeclarationWithClassDecorator.ts] //// + +=== anonymousClassDeclarationWithClassDecorator.ts === +declare var dec: any; +>dec : Symbol(dec, Decl(anonymousClassDeclarationWithClassDecorator.ts, 0, 11)) + +@dec +>dec : Symbol(dec, Decl(anonymousClassDeclarationWithClassDecorator.ts, 0, 11)) + +class { +} + diff --git a/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.types b/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.types new file mode 100644 index 0000000000000..f1e1933daece5 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.types @@ -0,0 +1,12 @@ +//// [tests/cases/compiler/anonymousClassDeclarationWithClassDecorator.ts] //// + +=== anonymousClassDeclarationWithClassDecorator.ts === +declare var dec: any; +>dec : any + +@dec +>dec : any + +class { +} + diff --git a/tsc/testdata/tests/cases/compiler/anonymousClassDeclarationWithClassDecorator.ts b/tsc/testdata/tests/cases/compiler/anonymousClassDeclarationWithClassDecorator.ts new file mode 100644 index 0000000000000..2572cebb19766 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/anonymousClassDeclarationWithClassDecorator.ts @@ -0,0 +1,7 @@ +// @target: es2022 + +declare var dec: any; + +@dec +class { +} From f30bf57b47105a4e2809e42eb43a562c0d11760b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 24 Aug 2026 08:43:21 +0200 Subject: [PATCH 2/4] Preserve anonymous default class decorator emit --- tsc/internal/transformers/tstransforms/runtimesyntax.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tsc/internal/transformers/tstransforms/runtimesyntax.go b/tsc/internal/transformers/tstransforms/runtimesyntax.go index 7dc491ce46a8a..c4950cb24bdbe 100644 --- a/tsc/internal/transformers/tstransforms/runtimesyntax.go +++ b/tsc/internal/transformers/tstransforms/runtimesyntax.go @@ -676,7 +676,10 @@ func (tx *RuntimeSyntaxTransformer) visitClassDeclaration(node *ast.ClassDeclara } name := tx.Visitor().VisitNode(node.Name()) - if name == nil && (exported || ast.NodeOrChildIsDecorated(tx.compilerOptions.ExperimentalDecorators.IsTrue(), node.AsNode(), nil, nil)) { + isDefault := ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsDefault) + isDecorated := ast.ChildIsDecorated(tx.compilerOptions.ExperimentalDecorators.IsTrue(), node.AsNode(), nil) || + !isDefault && ast.NodeIsDecorated(tx.compilerOptions.ExperimentalDecorators.IsTrue(), node.AsNode(), nil, nil) + if name == nil && (exported || isDecorated) { name = tx.Factory().NewGeneratedNameForNode(node.AsNode()) } heritageClauses := tx.Visitor().VisitNodes(node.HeritageClauses) From d80f2fd614b64baee99410bd7b10dae822933464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Tue, 25 Aug 2026 08:40:55 +0200 Subject: [PATCH 3/4] Handle decorated default class without export --- .../tstransforms/runtimesyntax.go | 12 ++++++----- ...ssDeclarationWithClassDecorator.errors.txt | 9 +++++++- ...ymousClassDeclarationWithClassDecorator.js | 21 +++++++++++++++++++ ...ClassDeclarationWithClassDecorator.symbols | 6 ++++++ ...usClassDeclarationWithClassDecorator.types | 6 ++++++ ...ymousClassDeclarationWithClassDecorator.ts | 4 ++++ 6 files changed, 52 insertions(+), 6 deletions(-) diff --git a/tsc/internal/transformers/tstransforms/runtimesyntax.go b/tsc/internal/transformers/tstransforms/runtimesyntax.go index c4950cb24bdbe..101890a98fb42 100644 --- a/tsc/internal/transformers/tstransforms/runtimesyntax.go +++ b/tsc/internal/transformers/tstransforms/runtimesyntax.go @@ -676,11 +676,13 @@ func (tx *RuntimeSyntaxTransformer) visitClassDeclaration(node *ast.ClassDeclara } name := tx.Visitor().VisitNode(node.Name()) - isDefault := ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsDefault) - isDecorated := ast.ChildIsDecorated(tx.compilerOptions.ExperimentalDecorators.IsTrue(), node.AsNode(), nil) || - !isDefault && ast.NodeIsDecorated(tx.compilerOptions.ExperimentalDecorators.IsTrue(), node.AsNode(), nil, nil) - if name == nil && (exported || isDecorated) { - name = tx.Factory().NewGeneratedNameForNode(node.AsNode()) + if name == nil { + isDefaultExport := node.ModifierFlags()&ast.ModifierFlagsExportDefault == ast.ModifierFlagsExportDefault + if exported || + ast.ChildIsDecorated(tx.compilerOptions.ExperimentalDecorators.IsTrue(), node.AsNode(), nil) || + !isDefaultExport && ast.NodeIsDecorated(tx.compilerOptions.ExperimentalDecorators.IsTrue(), node.AsNode(), nil, nil) { + name = tx.Factory().NewGeneratedNameForNode(node.AsNode()) + } } heritageClauses := tx.Visitor().VisitNodes(node.HeritageClauses) members := tx.Visitor().VisitNodes(node.Members) diff --git a/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.errors.txt b/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.errors.txt index ee8031adaaa2b..4d3b3094a5b8f 100644 --- a/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.errors.txt @@ -1,7 +1,8 @@ anonymousClassDeclarationWithClassDecorator.ts(3,1): error TS1211: A class declaration without the 'default' modifier must have a name. +anonymousClassDeclarationWithClassDecorator.ts(8,1): error TS1029: 'export' modifier must precede 'default' modifier. -==== anonymousClassDeclarationWithClassDecorator.ts (1 errors) ==== +==== anonymousClassDeclarationWithClassDecorator.ts (2 errors) ==== declare var dec: any; @dec @@ -9,4 +10,10 @@ anonymousClassDeclarationWithClassDecorator.ts(3,1): error TS1211: A class decla !!! error TS1211: A class declaration without the 'default' modifier must have a name. class { } + + @dec + default class { + ~~~~~~~ +!!! error TS1029: 'export' modifier must precede 'default' modifier. + } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.js b/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.js index 380f11c60d845..f86744e8696bc 100644 --- a/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.js +++ b/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.js @@ -6,6 +6,10 @@ declare var dec: any; @dec class { } + +@dec +default class { +} //// [anonymousClassDeclarationWithClassDecorator.js] @@ -61,3 +65,20 @@ let default_1 = (() => { }; return default_1 = _classThis; })(); +default let default_2 = (() => { + let _classDecorators = [dec]; + let _classDescriptor; + let _classExtraInitializers = []; + let _classThis; + var default_2 = class { + static { _classThis = this; } + static { + const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; + __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); + default_2 = _classThis = _classDescriptor.value; + if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); + __runInitializers(_classThis, _classExtraInitializers); + } + }; + return default_2 = _classThis; +})(); diff --git a/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.symbols b/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.symbols index f5afbe551f012..6226357f045dd 100644 --- a/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.symbols +++ b/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.symbols @@ -10,3 +10,9 @@ declare var dec: any; class { } +@dec +>dec : Symbol(dec, Decl(anonymousClassDeclarationWithClassDecorator.ts, 0, 11)) + +default class { +} + diff --git a/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.types b/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.types index f1e1933daece5..5a49ff16e2d67 100644 --- a/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.types +++ b/tsc/testdata/baselines/reference/compiler/anonymousClassDeclarationWithClassDecorator.types @@ -10,3 +10,9 @@ declare var dec: any; class { } +@dec +>dec : any + +default class { +} + diff --git a/tsc/testdata/tests/cases/compiler/anonymousClassDeclarationWithClassDecorator.ts b/tsc/testdata/tests/cases/compiler/anonymousClassDeclarationWithClassDecorator.ts index 2572cebb19766..e2ac4135013d6 100644 --- a/tsc/testdata/tests/cases/compiler/anonymousClassDeclarationWithClassDecorator.ts +++ b/tsc/testdata/tests/cases/compiler/anonymousClassDeclarationWithClassDecorator.ts @@ -5,3 +5,7 @@ declare var dec: any; @dec class { } + +@dec +default class { +} From 6a3799c1814a432f00bf20a94e1edf5ef79cd4a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Tue, 25 Aug 2026 08:57:20 +0200 Subject: [PATCH 4/4] Simplify anonymous class name condition --- .../transformers/tstransforms/runtimesyntax.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tsc/internal/transformers/tstransforms/runtimesyntax.go b/tsc/internal/transformers/tstransforms/runtimesyntax.go index 101890a98fb42..360a39e323b86 100644 --- a/tsc/internal/transformers/tstransforms/runtimesyntax.go +++ b/tsc/internal/transformers/tstransforms/runtimesyntax.go @@ -676,13 +676,11 @@ func (tx *RuntimeSyntaxTransformer) visitClassDeclaration(node *ast.ClassDeclara } name := tx.Visitor().VisitNode(node.Name()) - if name == nil { - isDefaultExport := node.ModifierFlags()&ast.ModifierFlagsExportDefault == ast.ModifierFlagsExportDefault - if exported || - ast.ChildIsDecorated(tx.compilerOptions.ExperimentalDecorators.IsTrue(), node.AsNode(), nil) || - !isDefaultExport && ast.NodeIsDecorated(tx.compilerOptions.ExperimentalDecorators.IsTrue(), node.AsNode(), nil, nil) { - name = tx.Factory().NewGeneratedNameForNode(node.AsNode()) - } + if name == nil && (exported || + ast.ChildIsDecorated(tx.compilerOptions.ExperimentalDecorators.IsTrue(), node.AsNode(), nil) || + node.ModifierFlags()&ast.ModifierFlagsExportDefault != ast.ModifierFlagsExportDefault && + ast.NodeIsDecorated(tx.compilerOptions.ExperimentalDecorators.IsTrue(), node.AsNode(), nil, nil)) { + name = tx.Factory().NewGeneratedNameForNode(node.AsNode()) } heritageClauses := tx.Visitor().VisitNodes(node.HeritageClauses) members := tx.Visitor().VisitNodes(node.Members)