diff --git a/src/compiler/transformers/legacyDecorators.ts b/src/compiler/transformers/legacyDecorators.ts index 1669b9a343f14..f175fe71a1951 100644 --- a/src/compiler/transformers/legacyDecorators.ts +++ b/src/compiler/transformers/legacyDecorators.ts @@ -37,6 +37,7 @@ import { isBlock, isCallToHelper, isClassElement, + isClassStaticBlockDeclaration, isComputedPropertyName, isDecorator, isExportOrDefaultModifier, @@ -339,6 +340,27 @@ export function transformLegacyDecorators(context: TransformationContext): (x: S let decorationStatements: Statement[] | undefined = []; ({ members, decorationStatements } = transformDecoratorsOfClassElements(node, members)); + // If we're emitting to ES2022 or later then we need to reassign the class alias before + // static initializers are evaluated. + const assignClassAliasInStaticBlock = + languageVersion >= ScriptTarget.ES2022 && + !!classAlias && + some(members, member => + isPropertyDeclaration(member) && hasSyntacticModifier(member, ModifierFlags.Static) || + isClassStaticBlockDeclaration(member)); + if (assignClassAliasInStaticBlock) { + members = setTextRange(factory.createNodeArray([ + factory.createClassStaticBlockDeclaration( + factory.createBlock([ + factory.createExpressionStatement( + factory.createAssignment(classAlias, factory.createThis()) + ) + ]) + ), + ...members + ]), members); + } + const classExpression = factory.createClassExpression( modifiers, name && isGeneratedIdentifier(name) ? undefined : name, @@ -355,7 +377,7 @@ export function transformLegacyDecorators(context: TransformationContext): (x: S declName, /*exclamationToken*/ undefined, /*type*/ undefined, - classAlias ? factory.createAssignment(classAlias, classExpression) : classExpression + classAlias && !assignClassAliasInStaticBlock ? factory.createAssignment(classAlias, classExpression) : classExpression ); setOriginalNode(varDecl, node); diff --git a/tests/baselines/reference/staticInitializersAndLegacyClassDecorators.js b/tests/baselines/reference/staticInitializersAndLegacyClassDecorators.js new file mode 100644 index 0000000000000..6c7a5099a605c --- /dev/null +++ b/tests/baselines/reference/staticInitializersAndLegacyClassDecorators.js @@ -0,0 +1,43 @@ +//// [staticInitializersAndLegacyClassDecorators.ts] +// https://github.com/microsoft/TypeScript/issues/52004 +declare var dec: any; + +@dec +class C1 +{ + static instance = new C1(); +} + +@dec +class C2 +{ + static { + new C2(); + } +} + + +//// [staticInitializersAndLegacyClassDecorators.js] +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 C1_1, C2_1; +let C1 = class C1 { + static { C1_1 = this; } + static instance = new C1_1(); +}; +C1 = C1_1 = __decorate([ + dec +], C1); +let C2 = class C2 { + static { C2_1 = this; } + static { + new C2_1(); + } +}; +C2 = C2_1 = __decorate([ + dec +], C2); diff --git a/tests/cases/compiler/staticInitializersAndLegacyClassDecorators.ts b/tests/cases/compiler/staticInitializersAndLegacyClassDecorators.ts new file mode 100644 index 0000000000000..a6381a89809a7 --- /dev/null +++ b/tests/cases/compiler/staticInitializersAndLegacyClassDecorators.ts @@ -0,0 +1,20 @@ +// @target: es2022 +// @experimentalDecorators: true +// @noTypesAndSymbols: true + +// https://github.com/microsoft/TypeScript/issues/52004 +declare var dec: any; + +@dec +class C1 +{ + static instance = new C1(); +} + +@dec +class C2 +{ + static { + new C2(); + } +}