@@ -135,10 +135,13 @@ namespace ts {
135135
136136 const shouldTransformPrivateElementsOrClassStaticBlocks = languageVersion < ScriptTarget . ES2022 ;
137137
138+ // We need to transform `this` in a static initializer into a reference to the class
139+ // when targeting < ES2022 since the assignment will be moved outside of the class body.
140+ const shouldTransformThisInStaticInitializers = languageVersion < ScriptTarget . ES2022 ;
141+
138142 // We don't need to transform `super` property access when targeting ES5, ES3 because
139143 // the es2015 transformation handles those.
140- const shouldTransformSuperInStaticInitializers = ( languageVersion <= ScriptTarget . ES2021 || ! useDefineForClassFields ) && languageVersion >= ScriptTarget . ES2015 ;
141- const shouldTransformThisInStaticInitializers = languageVersion <= ScriptTarget . ES2021 || ! useDefineForClassFields ;
144+ const shouldTransformSuperInStaticInitializers = shouldTransformThisInStaticInitializers && languageVersion >= ScriptTarget . ES2015 ;
142145
143146 const previousOnSubstituteNode = context . onSubstituteNode ;
144147 context . onSubstituteNode = onSubstituteNode ;
@@ -422,6 +425,11 @@ namespace ts {
422425
423426 if ( isPrivateIdentifier ( node . name ) ) {
424427 if ( ! shouldTransformPrivateElementsOrClassStaticBlocks ) {
428+ if ( isStatic ( node ) ) {
429+ // static fields are left as is
430+ return visitEachChild ( node , visitor , context ) ;
431+ }
432+
425433 // Initializer is elided as the field is initialized in transformConstructor.
426434 return factory . updatePropertyDeclaration (
427435 node ,
@@ -448,6 +456,28 @@ namespace ts {
448456 if ( expr && ! isSimpleInlineableExpression ( expr ) ) {
449457 getPendingExpressions ( ) . push ( expr ) ;
450458 }
459+
460+ if ( isStatic ( node ) && ! shouldTransformPrivateElementsOrClassStaticBlocks && ! useDefineForClassFields ) {
461+ const initializerStatement = transformPropertyOrClassStaticBlock ( node , factory . createThis ( ) ) ;
462+ if ( initializerStatement ) {
463+ const staticBlock = factory . createClassStaticBlockDeclaration (
464+ /*decorators*/ undefined ,
465+ /*modifiers*/ undefined ,
466+ factory . createBlock ( [ initializerStatement ] )
467+ ) ;
468+
469+ setOriginalNode ( staticBlock , node ) ;
470+ setCommentRange ( staticBlock , node ) ;
471+
472+ // Set the comment range for the statement to an empty synthetic range
473+ // and drop synthetic comments from the statement to avoid printing them twice.
474+ setCommentRange ( initializerStatement , { pos : - 1 , end : - 1 } ) ;
475+ setSyntheticLeadingComments ( initializerStatement , undefined ) ;
476+ setSyntheticTrailingComments ( initializerStatement , undefined ) ;
477+ return staticBlock ;
478+ }
479+ }
480+
451481 return undefined ;
452482 }
453483
@@ -1006,8 +1036,6 @@ namespace ts {
10061036 enableSubstitutionForClassStaticThisOrSuperReference ( ) ;
10071037 }
10081038
1009- const staticProperties = getStaticPropertiesAndClassStaticBlock ( node ) ;
1010-
10111039 // If a class has private static fields, or a static field has a `this` or `super` reference,
10121040 // then we need to allocate a temp variable to hold on to that reference.
10131041 let pendingClassReferenceAssignment : BinaryExpression | undefined ;
@@ -1047,6 +1075,7 @@ namespace ts {
10471075 // HasLexicalDeclaration (N) : Determines if the argument identifier has a binding in this environment record that was created using
10481076 // a lexical declaration such as a LexicalDeclaration or a ClassDeclaration.
10491077
1078+ const staticProperties = getStaticPropertiesAndClassStaticBlock ( node ) ;
10501079 if ( some ( staticProperties ) ) {
10511080 addPropertyOrClassStaticBlockStatements ( statements , staticProperties , factory . getInternalName ( node ) ) ;
10521081 }
@@ -1102,7 +1131,7 @@ namespace ts {
11021131 transformClassMembers ( node , isDerivedClass )
11031132 ) ;
11041133
1105- const hasTransformableStatics = some ( staticPropertiesOrClassStaticBlocks , p => isClassStaticBlockDeclaration ( p ) || ! ! p . initializer || ( shouldTransformPrivateElementsOrClassStaticBlocks && isPrivateIdentifier ( p . name ) ) ) ;
1134+ const hasTransformableStatics = shouldTransformPrivateElementsOrClassStaticBlocks && some ( staticPropertiesOrClassStaticBlocks , p => isClassStaticBlockDeclaration ( p ) || ! ! p . initializer || isPrivateIdentifier ( p . name ) ) ;
11061135 if ( hasTransformableStatics || some ( pendingExpressions ) ) {
11071136 if ( isDecoratedClassDeclaration ) {
11081137 Debug . assertIsDefined ( pendingStatements , "Decorated classes transformed by TypeScript are expected to be within a variable declaration." ) ;
@@ -1156,6 +1185,7 @@ namespace ts {
11561185 }
11571186
11581187 function transformClassMembers ( node : ClassDeclaration | ClassExpression , isDerivedClass : boolean ) {
1188+ const members : ClassElement [ ] = [ ] ;
11591189 if ( shouldTransformPrivateElementsOrClassStaticBlocks ) {
11601190 // Declare private names.
11611191 for ( const member of node . members ) {
@@ -1169,12 +1199,26 @@ namespace ts {
11691199 }
11701200 }
11711201
1172- const members : ClassElement [ ] = [ ] ;
11731202 const constructor = transformConstructor ( node , isDerivedClass ) ;
1203+ const visitedMembers = visitNodes ( node . members , classElementVisitor , isClassElement ) ;
1204+
11741205 if ( constructor ) {
11751206 members . push ( constructor ) ;
11761207 }
1177- addRange ( members , visitNodes ( node . members , classElementVisitor , isClassElement ) ) ;
1208+
1209+ if ( ! shouldTransformPrivateElementsOrClassStaticBlocks && some ( pendingExpressions ) ) {
1210+ members . push ( factory . createClassStaticBlockDeclaration (
1211+ /*decorators*/ undefined ,
1212+ /*modifiers*/ undefined ,
1213+ factory . createBlock ( [
1214+ factory . createExpressionStatement ( factory . inlineExpressions ( pendingExpressions ) )
1215+ ] )
1216+ ) ) ;
1217+ pendingExpressions = undefined ;
1218+ }
1219+
1220+ addRange ( members , visitedMembers ) ;
1221+
11781222 return setTextRange ( factory . createNodeArray ( members ) , /*location*/ node . members ) ;
11791223 }
11801224
@@ -1374,27 +1418,41 @@ namespace ts {
13741418 */
13751419 function addPropertyOrClassStaticBlockStatements ( statements : Statement [ ] , properties : readonly ( PropertyDeclaration | ClassStaticBlockDeclaration ) [ ] , receiver : LeftHandSideExpression ) {
13761420 for ( const property of properties ) {
1377- const expression = isClassStaticBlockDeclaration ( property ) ?
1378- transformClassStaticBlockDeclaration ( property ) :
1379- transformProperty ( property , receiver ) ;
1380- if ( ! expression ) {
1421+ if ( isStatic ( property ) && ! shouldTransformPrivateElementsOrClassStaticBlocks && ! useDefineForClassFields ) {
13811422 continue ;
13821423 }
1383- const statement = factory . createExpressionStatement ( expression ) ;
1384- setSourceMapRange ( statement , moveRangePastModifiers ( property ) ) ;
1385- setCommentRange ( statement , property ) ;
1386- setOriginalNode ( statement , property ) ;
13871424
1388- // `setOriginalNode` *copies* the `emitNode` from `property`, so now both
1389- // `statement` and `expression` have a copy of the synthesized comments.
1390- // Drop the comments from expression to avoid printing them twice.
1391- setSyntheticLeadingComments ( expression , undefined ) ;
1392- setSyntheticTrailingComments ( expression , undefined ) ;
1425+ const statement = transformPropertyOrClassStaticBlock ( property , receiver ) ;
1426+ if ( ! statement ) {
1427+ continue ;
1428+ }
13931429
13941430 statements . push ( statement ) ;
13951431 }
13961432 }
13971433
1434+ function transformPropertyOrClassStaticBlock ( property : PropertyDeclaration | ClassStaticBlockDeclaration , receiver : LeftHandSideExpression ) {
1435+ const expression = isClassStaticBlockDeclaration ( property ) ?
1436+ transformClassStaticBlockDeclaration ( property ) :
1437+ transformProperty ( property , receiver ) ;
1438+ if ( ! expression ) {
1439+ return undefined ;
1440+ }
1441+
1442+ const statement = factory . createExpressionStatement ( expression ) ;
1443+ setSourceMapRange ( statement , moveRangePastModifiers ( property ) ) ;
1444+ setCommentRange ( statement , property ) ;
1445+ setOriginalNode ( statement , property ) ;
1446+
1447+ // `setOriginalNode` *copies* the `emitNode` from `property`, so now both
1448+ // `statement` and `expression` have a copy of the synthesized comments.
1449+ // Drop the comments from expression to avoid printing them twice.
1450+ setSyntheticLeadingComments ( expression , undefined ) ;
1451+ setSyntheticTrailingComments ( expression , undefined ) ;
1452+
1453+ return statement ;
1454+ }
1455+
13981456 /**
13991457 * Generates assignment expressions for property initializers.
14001458 *
0 commit comments