@@ -24,6 +24,11 @@ namespace ts {
24
24
* Stores if the identifier is static or not
25
25
*/
26
26
isStatic : boolean ;
27
+ /**
28
+ * Stores if the identifier declaration is valid or not. Reserved names (e.g. #constructor)
29
+ * or duplicate identifiers are considered invalid.
30
+ */
31
+ isValid : boolean ;
27
32
}
28
33
interface PrivateIdentifierAccessorInfo extends PrivateIdentifierInfoBase {
29
34
kind : PrivateIdentifierKind . Accessor ;
@@ -260,6 +265,13 @@ namespace ts {
260
265
return visitEachChild ( node , classElementVisitor , context ) ;
261
266
}
262
267
268
+ // leave invalid code untransformed
269
+ const info = accessPrivateIdentifier ( node . name ) ;
270
+ Debug . assert ( info , "Undeclared private name for property declaration." ) ;
271
+ if ( ! info . isValid ) {
272
+ return node ;
273
+ }
274
+
263
275
const functionName = getHoistedFunctionName ( node ) ;
264
276
if ( functionName ) {
265
277
getPendingExpressions ( ) . push (
@@ -303,17 +315,27 @@ namespace ts {
303
315
304
316
function visitPropertyDeclaration ( node : PropertyDeclaration ) {
305
317
Debug . assert ( ! some ( node . decorators ) ) ;
306
- if ( ! shouldTransformPrivateElements && isPrivateIdentifier ( node . name ) ) {
307
- // Initializer is elided as the field is initialized in transformConstructor.
308
- return factory . updatePropertyDeclaration (
309
- node ,
310
- /*decorators*/ undefined ,
311
- visitNodes ( node . modifiers , visitor , isModifier ) ,
312
- node . name ,
313
- /*questionOrExclamationToken*/ undefined ,
314
- /*type*/ undefined ,
315
- /*initializer*/ undefined
316
- ) ;
318
+
319
+ if ( isPrivateIdentifier ( node . name ) ) {
320
+ if ( ! shouldTransformPrivateElements ) {
321
+ // Initializer is elided as the field is initialized in transformConstructor.
322
+ return factory . updatePropertyDeclaration (
323
+ node ,
324
+ /*decorators*/ undefined ,
325
+ visitNodes ( node . modifiers , visitor , isModifier ) ,
326
+ node . name ,
327
+ /*questionOrExclamationToken*/ undefined ,
328
+ /*type*/ undefined ,
329
+ /*initializer*/ undefined
330
+ ) ;
331
+ }
332
+
333
+ // leave invalid code untransformed
334
+ const info = accessPrivateIdentifier ( node . name ) ;
335
+ Debug . assert ( info , "Undeclared private name for property declaration." ) ;
336
+ if ( ! info . isValid ) {
337
+ return node ;
338
+ }
317
339
}
318
340
// Create a temporary variable to store a computed property name (if necessary).
319
341
// If it's not inlineable, then we emit an expression after the class which assigns
@@ -1162,55 +1184,62 @@ namespace ts {
1162
1184
const env = getPrivateIdentifierEnvironment ( ) ;
1163
1185
const { weakSetName, classConstructor } = env ;
1164
1186
const assignmentExpressions : Expression [ ] = [ ] ;
1187
+
1188
+ const privateName = node . name . escapedText ;
1189
+ const previousInfo = env . identifiers . get ( privateName ) ;
1190
+ const isValid = ! isReservedPrivateName ( node . name ) && previousInfo === undefined ;
1191
+
1165
1192
if ( hasStaticModifier ( node ) ) {
1166
1193
Debug . assert ( classConstructor , "weakSetName should be set in private identifier environment" ) ;
1167
1194
if ( isPropertyDeclaration ( node ) ) {
1168
1195
const variableName = createHoistedVariableForPrivateName ( text , node ) ;
1169
- env . identifiers . set ( node . name . escapedText , {
1196
+ env . identifiers . set ( privateName , {
1170
1197
kind : PrivateIdentifierKind . Field ,
1171
1198
variableName,
1172
1199
brandCheckIdentifier : classConstructor ,
1173
1200
isStatic : true ,
1201
+ isValid,
1174
1202
} ) ;
1175
1203
}
1176
1204
else if ( isMethodDeclaration ( node ) ) {
1177
1205
const functionName = createHoistedVariableForPrivateName ( text , node ) ;
1178
- env . identifiers . set ( node . name . escapedText , {
1206
+ env . identifiers . set ( privateName , {
1179
1207
kind : PrivateIdentifierKind . Method ,
1180
1208
methodName : functionName ,
1181
1209
brandCheckIdentifier : classConstructor ,
1182
1210
isStatic : true ,
1211
+ isValid,
1183
1212
} ) ;
1184
1213
}
1185
1214
else if ( isGetAccessorDeclaration ( node ) ) {
1186
1215
const getterName = createHoistedVariableForPrivateName ( text + "_get" , node ) ;
1187
- const previousInfo = env . identifiers . get ( node . name . escapedText ) ;
1188
- if ( previousInfo ?. kind === PrivateIdentifierKind . Accessor ) {
1216
+ if ( previousInfo ?. kind === PrivateIdentifierKind . Accessor && previousInfo . isStatic && ! previousInfo . getterName ) {
1189
1217
previousInfo . getterName = getterName ;
1190
1218
}
1191
1219
else {
1192
- env . identifiers . set ( node . name . escapedText , {
1220
+ env . identifiers . set ( privateName , {
1193
1221
kind : PrivateIdentifierKind . Accessor ,
1194
1222
getterName,
1195
1223
setterName : undefined ,
1196
1224
brandCheckIdentifier : classConstructor ,
1197
1225
isStatic : true ,
1226
+ isValid,
1198
1227
} ) ;
1199
1228
}
1200
1229
}
1201
1230
else if ( isSetAccessorDeclaration ( node ) ) {
1202
1231
const setterName = createHoistedVariableForPrivateName ( text + "_set" , node ) ;
1203
- const previousInfo = env . identifiers . get ( node . name . escapedText ) ;
1204
- if ( previousInfo ?. kind === PrivateIdentifierKind . Accessor ) {
1232
+ if ( previousInfo ?. kind === PrivateIdentifierKind . Accessor && previousInfo . isStatic && ! previousInfo . setterName ) {
1205
1233
previousInfo . setterName = setterName ;
1206
1234
}
1207
1235
else {
1208
- env . identifiers . set ( node . name . escapedText , {
1236
+ env . identifiers . set ( privateName , {
1209
1237
kind : PrivateIdentifierKind . Accessor ,
1210
1238
getterName : undefined ,
1211
1239
setterName,
1212
1240
brandCheckIdentifier : classConstructor ,
1213
1241
isStatic : true ,
1242
+ isValid,
1214
1243
} ) ;
1215
1244
}
1216
1245
}
@@ -1220,11 +1249,12 @@ namespace ts {
1220
1249
}
1221
1250
else if ( isPropertyDeclaration ( node ) ) {
1222
1251
const weakMapName = createHoistedVariableForPrivateName ( text , node ) ;
1223
- env . identifiers . set ( node . name . escapedText , {
1252
+ env . identifiers . set ( privateName , {
1224
1253
kind : PrivateIdentifierKind . Field ,
1225
1254
brandCheckIdentifier : weakMapName ,
1226
1255
isStatic : false ,
1227
- variableName : undefined
1256
+ variableName : undefined ,
1257
+ isValid,
1228
1258
} ) ;
1229
1259
1230
1260
assignmentExpressions . push ( factory . createAssignment (
@@ -1239,46 +1269,48 @@ namespace ts {
1239
1269
else if ( isMethodDeclaration ( node ) ) {
1240
1270
Debug . assert ( weakSetName , "weakSetName should be set in private identifier environment" ) ;
1241
1271
1242
- env . identifiers . set ( node . name . escapedText , {
1272
+ env . identifiers . set ( privateName , {
1243
1273
kind : PrivateIdentifierKind . Method ,
1244
1274
methodName : createHoistedVariableForPrivateName ( text , node ) ,
1245
1275
brandCheckIdentifier : weakSetName ,
1246
1276
isStatic : false ,
1277
+ isValid,
1247
1278
} ) ;
1248
1279
}
1249
1280
else if ( isAccessor ( node ) ) {
1250
1281
Debug . assert ( weakSetName , "weakSetName should be set in private identifier environment" ) ;
1251
- const previousInfo = env . identifiers . get ( node . name . escapedText ) ;
1252
1282
1253
1283
if ( isGetAccessor ( node ) ) {
1254
1284
const getterName = createHoistedVariableForPrivateName ( text + "_get" , node ) ;
1255
1285
1256
- if ( previousInfo ?. kind === PrivateIdentifierKind . Accessor ) {
1286
+ if ( previousInfo ?. kind === PrivateIdentifierKind . Accessor && ! previousInfo . isStatic && ! previousInfo . getterName ) {
1257
1287
previousInfo . getterName = getterName ;
1258
1288
}
1259
1289
else {
1260
- env . identifiers . set ( node . name . escapedText , {
1290
+ env . identifiers . set ( privateName , {
1261
1291
kind : PrivateIdentifierKind . Accessor ,
1262
1292
getterName,
1263
1293
setterName : undefined ,
1264
1294
brandCheckIdentifier : weakSetName ,
1265
1295
isStatic : false ,
1296
+ isValid,
1266
1297
} ) ;
1267
1298
}
1268
1299
}
1269
1300
else {
1270
1301
const setterName = createHoistedVariableForPrivateName ( text + "_set" , node ) ;
1271
1302
1272
- if ( previousInfo ?. kind === PrivateIdentifierKind . Accessor ) {
1303
+ if ( previousInfo ?. kind === PrivateIdentifierKind . Accessor && ! previousInfo . isStatic && ! previousInfo . setterName ) {
1273
1304
previousInfo . setterName = setterName ;
1274
1305
}
1275
1306
else {
1276
- env . identifiers . set ( node . name . escapedText , {
1307
+ env . identifiers . set ( privateName , {
1277
1308
kind : PrivateIdentifierKind . Accessor ,
1278
1309
getterName : undefined ,
1279
1310
setterName,
1280
1311
brandCheckIdentifier : weakSetName ,
1281
1312
isStatic : false ,
1313
+ isValid,
1282
1314
} ) ;
1283
1315
}
1284
1316
}
@@ -1475,4 +1507,8 @@ namespace ts {
1475
1507
[ receiver ]
1476
1508
) ;
1477
1509
}
1510
+
1511
+ function isReservedPrivateName ( node : PrivateIdentifier ) {
1512
+ return node . escapedText === "#constructor" ;
1513
+ }
1478
1514
}
0 commit comments