@@ -135,10 +135,13 @@ namespace ts {
135
135
136
136
const shouldTransformPrivateElementsOrClassStaticBlocks = languageVersion < ScriptTarget . ES2022 ;
137
137
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
+
138
142
// We don't need to transform `super` property access when targeting ES5, ES3 because
139
143
// 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 ;
142
145
143
146
const previousOnSubstituteNode = context . onSubstituteNode ;
144
147
context . onSubstituteNode = onSubstituteNode ;
@@ -422,6 +425,11 @@ namespace ts {
422
425
423
426
if ( isPrivateIdentifier ( node . name ) ) {
424
427
if ( ! shouldTransformPrivateElementsOrClassStaticBlocks ) {
428
+ if ( isStatic ( node ) ) {
429
+ // static fields are left as is
430
+ return visitEachChild ( node , visitor , context ) ;
431
+ }
432
+
425
433
// Initializer is elided as the field is initialized in transformConstructor.
426
434
return factory . updatePropertyDeclaration (
427
435
node ,
@@ -448,6 +456,28 @@ namespace ts {
448
456
if ( expr && ! isSimpleInlineableExpression ( expr ) ) {
449
457
getPendingExpressions ( ) . push ( expr ) ;
450
458
}
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
+
451
481
return undefined ;
452
482
}
453
483
@@ -1006,8 +1036,6 @@ namespace ts {
1006
1036
enableSubstitutionForClassStaticThisOrSuperReference ( ) ;
1007
1037
}
1008
1038
1009
- const staticProperties = getStaticPropertiesAndClassStaticBlock ( node ) ;
1010
-
1011
1039
// If a class has private static fields, or a static field has a `this` or `super` reference,
1012
1040
// then we need to allocate a temp variable to hold on to that reference.
1013
1041
let pendingClassReferenceAssignment : BinaryExpression | undefined ;
@@ -1047,6 +1075,7 @@ namespace ts {
1047
1075
// HasLexicalDeclaration (N) : Determines if the argument identifier has a binding in this environment record that was created using
1048
1076
// a lexical declaration such as a LexicalDeclaration or a ClassDeclaration.
1049
1077
1078
+ const staticProperties = getStaticPropertiesAndClassStaticBlock ( node ) ;
1050
1079
if ( some ( staticProperties ) ) {
1051
1080
addPropertyOrClassStaticBlockStatements ( statements , staticProperties , factory . getInternalName ( node ) ) ;
1052
1081
}
@@ -1102,7 +1131,7 @@ namespace ts {
1102
1131
transformClassMembers ( node , isDerivedClass )
1103
1132
) ;
1104
1133
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 ) ) ;
1106
1135
if ( hasTransformableStatics || some ( pendingExpressions ) ) {
1107
1136
if ( isDecoratedClassDeclaration ) {
1108
1137
Debug . assertIsDefined ( pendingStatements , "Decorated classes transformed by TypeScript are expected to be within a variable declaration." ) ;
@@ -1156,6 +1185,7 @@ namespace ts {
1156
1185
}
1157
1186
1158
1187
function transformClassMembers ( node : ClassDeclaration | ClassExpression , isDerivedClass : boolean ) {
1188
+ const members : ClassElement [ ] = [ ] ;
1159
1189
if ( shouldTransformPrivateElementsOrClassStaticBlocks ) {
1160
1190
// Declare private names.
1161
1191
for ( const member of node . members ) {
@@ -1169,12 +1199,26 @@ namespace ts {
1169
1199
}
1170
1200
}
1171
1201
1172
- const members : ClassElement [ ] = [ ] ;
1173
1202
const constructor = transformConstructor ( node , isDerivedClass ) ;
1203
+ const visitedMembers = visitNodes ( node . members , classElementVisitor , isClassElement ) ;
1204
+
1174
1205
if ( constructor ) {
1175
1206
members . push ( constructor ) ;
1176
1207
}
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
+
1178
1222
return setTextRange ( factory . createNodeArray ( members ) , /*location*/ node . members ) ;
1179
1223
}
1180
1224
@@ -1374,27 +1418,41 @@ namespace ts {
1374
1418
*/
1375
1419
function addPropertyOrClassStaticBlockStatements ( statements : Statement [ ] , properties : readonly ( PropertyDeclaration | ClassStaticBlockDeclaration ) [ ] , receiver : LeftHandSideExpression ) {
1376
1420
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 ) {
1381
1422
continue ;
1382
1423
}
1383
- const statement = factory . createExpressionStatement ( expression ) ;
1384
- setSourceMapRange ( statement , moveRangePastModifiers ( property ) ) ;
1385
- setCommentRange ( statement , property ) ;
1386
- setOriginalNode ( statement , property ) ;
1387
1424
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
+ }
1393
1429
1394
1430
statements . push ( statement ) ;
1395
1431
}
1396
1432
}
1397
1433
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
+
1398
1456
/**
1399
1457
* Generates assignment expressions for property initializers.
1400
1458
*
0 commit comments