Skip to content

Commit af689cc

Browse files
authored
ES private field check (#44648)
* es private fields in in (#52) add support for the 'private-fields-in-in' TC39 proposal Signed-off-by: Ashley Claymore <[email protected]> * [fixup] include inToken when walking forEachChild(node, cb) * [squash] re-accept lib definition baseline changes * [squash] reduce if/else to ternary Signed-off-by: Ashley Claymore <[email protected]> * [squash] drop 'originalName' and rename parameter instead Signed-off-by: Ashley Claymore <[email protected]> * [squash] extend spelling suggestion to all privateIdentifiers Signed-off-by: Ashley Claymore <[email protected]> * [squash] revert the added lexical spelling suggestions logic Signed-off-by: Ashley Claymore <[email protected]> * [squash] update baseline Signed-off-by: Ashley Claymore <[email protected]> * [squash] inline variable as per PR suggestion Signed-off-by: Ashley Claymore <[email protected]> * [squash] test targets both esnext and es2020 as per PR comment Signed-off-by: Ashley Claymore <[email protected]> * switch to using a binary expression Signed-off-by: Ashley Claymore <[email protected]> * [squash] PrivateIdentifier now extends PrimaryExpression Signed-off-by: Ashley Claymore <[email protected]> * [squash] accept public api baseline changes Signed-off-by: Ashley Claymore <[email protected]> * [squash] classPrivateFieldInHelper now has documentation Signed-off-by: Ashley Claymore <[email protected]> * [squash] type-check now follows existing in-expression path Signed-off-by: Ashley Claymore <[email protected]> * [squash] parser now follows existing binaryExpression path Signed-off-by: Ashley Claymore <[email protected]> * [squash] correct typo in comment Signed-off-by: Ashley Claymore <[email protected]> * [squash] no longer use esNext flag Signed-off-by: Ashley Claymore <[email protected]> * [squash] swap 'reciever, state' helper params Signed-off-by: Ashley Claymore <[email protected]> * [squash] remove change to parenthesizerRules Signed-off-by: Ashley Claymore <[email protected]> * [squash] apply suggested changes to checker Signed-off-by: Ashley Claymore <[email protected]> * [squash] remove need for assertion in fixSpelling Signed-off-by: Ashley Claymore <[email protected]> * [squash] improve comment hint in test Signed-off-by: Ashley Claymore <[email protected]> * [squash] fix comment typos Signed-off-by: Ashley Claymore <[email protected]> * [squash] add flow-test for Foo | FooSub | Bar Signed-off-by: Ashley Claymore <[email protected]> * [squash] add checkExternalEmitHelpers call and new test case Signed-off-by: Ashley Claymore <[email protected]> * [squash] simplify and correct parser Signed-off-by: Ashley Claymore <[email protected]> * [squash] move most of the added checker logic to expression level Signed-off-by: Ashley Claymore <[email protected]> * [squash] always error when privateId could not be resolved Signed-off-by: Ashley Claymore <[email protected]> * [squash] reword comment Signed-off-by: Ashley Claymore <[email protected]> * [squash] fix codeFixSpelling test Signed-off-by: Ashley Claymore <[email protected]> * [squash] do less work Signed-off-by: Ashley Claymore <[email protected]> * store symbol by priateId not binaryExpression Signed-off-by: Ashley Claymore <[email protected]> * moved parsePrivateIdentifier into parsePrimaryExpression Signed-off-by: Ashley Claymore <[email protected]> * [squash] checkInExpressionn bails out early on silentNeverType Signed-off-by: Ashley Claymore <[email protected]> * [squash] more detailed error messages Signed-off-by: Ashley Claymore <[email protected]> * [squash] resolves conflict in diagnosticMessages.json Signed-off-by: Ashley Claymore <[email protected]> * [squash] update baseline for importHelpersES6 Signed-off-by: Ashley Claymore <[email protected]> * [squash] remove redundent if and comment from parser Signed-off-by: Ashley Claymore <[email protected]> * [squash] split up grammar/check/symbolLookup Signed-off-by: Ashley Claymore <[email protected]> * [squash] reword message for existing left side of in-expression error Signed-off-by: Ashley Claymore <[email protected]>
1 parent 59fb373 commit af689cc

File tree

48 files changed

+2398
-45
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2398
-45
lines changed

src/compiler/checker.ts

+82-7
Original file line numberDiff line numberDiff line change
@@ -23917,6 +23917,9 @@ namespace ts {
2391723917
case SyntaxKind.InstanceOfKeyword:
2391823918
return narrowTypeByInstanceof(type, expr, assumeTrue);
2391923919
case SyntaxKind.InKeyword:
23920+
if (isPrivateIdentifier(expr.left)) {
23921+
return narrowTypeByPrivateIdentifierInInExpression(type, expr, assumeTrue);
23922+
}
2392023923
const target = getReferenceCandidate(expr.right);
2392123924
const leftType = getTypeOfNode(expr.left);
2392223925
if (leftType.flags & TypeFlags.StringLiteral) {
@@ -23947,6 +23950,24 @@ namespace ts {
2394723950
return type;
2394823951
}
2394923952

23953+
function narrowTypeByPrivateIdentifierInInExpression(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
23954+
const target = getReferenceCandidate(expr.right);
23955+
if (!isMatchingReference(reference, target)) {
23956+
return type;
23957+
}
23958+
23959+
Debug.assertNode(expr.left, isPrivateIdentifier);
23960+
const symbol = getSymbolForPrivateIdentifierExpression(expr.left);
23961+
if (symbol === undefined) {
23962+
return type;
23963+
}
23964+
const classSymbol = symbol.parent!;
23965+
const targetType = hasStaticModifier(Debug.checkDefined(symbol.valueDeclaration, "should always have a declaration"))
23966+
? getTypeOfSymbol(classSymbol) as InterfaceType
23967+
: getDeclaredTypeOfSymbol(classSymbol);
23968+
return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom);
23969+
}
23970+
2395023971
function narrowTypeByOptionalChainContainment(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type {
2395123972
// We are in a branch of obj?.foo === value (or any one of the other equality operators). We narrow obj as follows:
2395223973
// When operator is === and type of value excludes undefined, null and undefined is removed from type of obj in true branch.
@@ -27790,6 +27811,40 @@ namespace ts {
2779027811
}
2779127812
}
2779227813

27814+
function checkGrammarPrivateIdentifierExpression(privId: PrivateIdentifier): boolean {
27815+
if (!getContainingClass(privId)) {
27816+
return grammarErrorOnNode(privId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies);
27817+
}
27818+
if (!isExpressionNode(privId)) {
27819+
return grammarErrorOnNode(privId, Diagnostics.Private_identifiers_are_only_allowed_in_class_bodies_and_may_only_be_used_as_part_of_a_class_member_declaration_property_access_or_on_the_left_hand_side_of_an_in_expression);
27820+
}
27821+
if (!getSymbolForPrivateIdentifierExpression(privId)) {
27822+
return grammarErrorOnNode(privId, Diagnostics.Cannot_find_name_0, idText(privId));
27823+
}
27824+
return false;
27825+
}
27826+
27827+
function checkPrivateIdentifierExpression(privId: PrivateIdentifier): Type {
27828+
checkGrammarPrivateIdentifierExpression(privId);
27829+
const symbol = getSymbolForPrivateIdentifierExpression(privId);
27830+
if (symbol) {
27831+
markPropertyAsReferenced(symbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false);
27832+
}
27833+
return anyType;
27834+
}
27835+
27836+
function getSymbolForPrivateIdentifierExpression(privId: PrivateIdentifier): Symbol | undefined {
27837+
if (!isExpressionNode(privId)) {
27838+
return undefined;
27839+
}
27840+
27841+
const links = getNodeLinks(privId);
27842+
if (links.resolvedSymbol === undefined) {
27843+
links.resolvedSymbol = lookupSymbolForPrivateIdentifierDeclaration(privId.escapedText, privId);
27844+
}
27845+
return links.resolvedSymbol;
27846+
}
27847+
2779327848
function getPrivateIdentifierPropertyOfType(leftType: Type, lexicallyScopedIdentifier: Symbol): Symbol | undefined {
2779427849
return getPropertyOfType(leftType, lexicallyScopedIdentifier.escapedName);
2779527850
}
@@ -32110,11 +32165,29 @@ namespace ts {
3211032165
if (leftType === silentNeverType || rightType === silentNeverType) {
3211132166
return silentNeverType;
3211232167
}
32113-
leftType = checkNonNullType(leftType, left);
32168+
if (isPrivateIdentifier(left)) {
32169+
if (languageVersion < ScriptTarget.ESNext) {
32170+
checkExternalEmitHelpers(left, ExternalEmitHelpers.ClassPrivateFieldIn);
32171+
}
32172+
// Unlike in 'checkPrivateIdentifierExpression' we now have access to the RHS type
32173+
// which provides us with the opportunity to emit more detailed errors
32174+
if (!getNodeLinks(left).resolvedSymbol && getContainingClass(left)) {
32175+
const isUncheckedJS = isUncheckedJSSuggestion(left, rightType.symbol, /*excludeClasses*/ true);
32176+
reportNonexistentProperty(left, rightType, isUncheckedJS);
32177+
}
32178+
}
32179+
else {
32180+
leftType = checkNonNullType(leftType, left);
32181+
// TypeScript 1.0 spec (April 2014): 4.15.5
32182+
// Require the left operand to be of type Any, the String primitive type, or the Number primitive type.
32183+
if (!(allTypesAssignableToKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike) ||
32184+
isTypeAssignableToKind(leftType, TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping | TypeFlags.TypeParameter))) {
32185+
error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_a_private_identifier_or_of_type_any_string_number_or_symbol);
32186+
}
32187+
}
3211432188
rightType = checkNonNullType(rightType, right);
3211532189
// TypeScript 1.0 spec (April 2014): 4.15.5
32116-
// The in operator requires the left operand to be of type Any, the String primitive type, or the Number primitive type,
32117-
// and the right operand to be
32190+
// The in operator requires the right operand to be
3211832191
//
3211932192
// 1. assignable to the non-primitive type,
3212032193
// 2. an unconstrained type parameter,
@@ -32132,10 +32205,6 @@ namespace ts {
3213232205
// unless *all* instantiations would result in an error.
3213332206
//
3213432207
// The result is always of the Boolean primitive type.
32135-
if (!(allTypesAssignableToKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike) ||
32136-
isTypeAssignableToKind(leftType, TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping | TypeFlags.TypeParameter))) {
32137-
error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol);
32138-
}
3213932208
const rightTypeConstraint = getConstraintOfType(rightType);
3214032209
if (!allTypesAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive) ||
3214132210
rightTypeConstraint && (
@@ -33517,6 +33586,8 @@ namespace ts {
3351733586
switch (kind) {
3351833587
case SyntaxKind.Identifier:
3351933588
return checkIdentifier(node as Identifier, checkMode);
33589+
case SyntaxKind.PrivateIdentifier:
33590+
return checkPrivateIdentifierExpression(node as PrivateIdentifier);
3352033591
case SyntaxKind.ThisKeyword:
3352133592
return checkThisExpression(node);
3352233593
case SyntaxKind.SuperKeyword:
@@ -40296,6 +40367,9 @@ namespace ts {
4029640367
}
4029740368
return result;
4029840369
}
40370+
else if (isPrivateIdentifier(name)) {
40371+
return getSymbolForPrivateIdentifierExpression(name);
40372+
}
4029940373
else if (name.kind === SyntaxKind.PropertyAccessExpression || name.kind === SyntaxKind.QualifiedName) {
4030040374
const links = getNodeLinks(name);
4030140375
if (links.resolvedSymbol) {
@@ -41712,6 +41786,7 @@ namespace ts {
4171241786
case ExternalEmitHelpers.MakeTemplateObject: return "__makeTemplateObject";
4171341787
case ExternalEmitHelpers.ClassPrivateFieldGet: return "__classPrivateFieldGet";
4171441788
case ExternalEmitHelpers.ClassPrivateFieldSet: return "__classPrivateFieldSet";
41789+
case ExternalEmitHelpers.ClassPrivateFieldIn: return "__classPrivateFieldIn";
4171541790
case ExternalEmitHelpers.CreateBinding: return "__createBinding";
4171641791
default: return Debug.fail("Unrecognized helper");
4171741792
}

src/compiler/diagnosticMessages.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -1392,6 +1392,10 @@
13921392
"category": "Message",
13931393
"code": 1450
13941394
},
1395+
"Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression": {
1396+
"category": "Error",
1397+
"code": 1451
1398+
},
13951399

13961400
"The types of '{0}' are incompatible between these types.": {
13971401
"category": "Error",
@@ -1654,7 +1658,7 @@
16541658
"category": "Error",
16551659
"code": 2359
16561660
},
1657-
"The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.": {
1661+
"The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.": {
16581662
"category": "Error",
16591663
"code": 2360
16601664
},

src/compiler/emitter.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1684,6 +1684,8 @@ namespace ts {
16841684
// Identifiers
16851685
case SyntaxKind.Identifier:
16861686
return emitIdentifier(node as Identifier);
1687+
case SyntaxKind.PrivateIdentifier:
1688+
return emitPrivateIdentifier(node as PrivateIdentifier);
16871689

16881690
// Expressions
16891691
case SyntaxKind.ArrayLiteralExpression:

src/compiler/factory/emitHelpers.ts

+30
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ namespace ts {
3434
// Class Fields Helpers
3535
createClassPrivateFieldGetHelper(receiver: Expression, state: Identifier, kind: PrivateIdentifierKind, f: Identifier | undefined): Expression;
3636
createClassPrivateFieldSetHelper(receiver: Expression, state: Identifier, value: Expression, kind: PrivateIdentifierKind, f: Identifier | undefined): Expression;
37+
createClassPrivateFieldInHelper(state: Identifier, receiver: Expression): Expression;
3738
}
3839

3940
export function createEmitHelperFactory(context: TransformationContext): EmitHelperFactory {
@@ -75,6 +76,7 @@ namespace ts {
7576
// Class Fields Helpers
7677
createClassPrivateFieldGetHelper,
7778
createClassPrivateFieldSetHelper,
79+
createClassPrivateFieldInHelper
7880
};
7981

8082
/**
@@ -395,6 +397,10 @@ namespace ts {
395397
return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldSet"), /*typeArguments*/ undefined, args);
396398
}
397399

400+
function createClassPrivateFieldInHelper(state: Identifier, receiver: Expression) {
401+
context.requestEmitHelper(classPrivateFieldInHelper);
402+
return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldIn"), /* typeArguments*/ undefined, [state, receiver]);
403+
}
398404
}
399405

400406
/* @internal */
@@ -961,6 +967,29 @@ namespace ts {
961967
};`
962968
};
963969

970+
/**
971+
* Parameters:
972+
* @param state — One of the following:
973+
* - A WeakMap when the member is a private instance field.
974+
* - A WeakSet when the member is a private instance method or accessor.
975+
* - A function value that should be the undecorated class constructor when the member is a private static field, method, or accessor.
976+
* @param receiver — The object being checked if it has the private member.
977+
*
978+
* Usage:
979+
* This helper is used to transform `#field in expression` to
980+
* `__classPrivateFieldIn(<weakMap/weakSet/constructor>, expression)`
981+
*/
982+
export const classPrivateFieldInHelper: UnscopedEmitHelper = {
983+
name: "typescript:classPrivateFieldIn",
984+
importName: "__classPrivateFieldIn",
985+
scoped: false,
986+
text: `
987+
var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(state, receiver) {
988+
if (receiver === null || (typeof receiver !== "object" && typeof receiver !== "function")) throw new TypeError("Cannot use 'in' operator on non-object");
989+
return typeof state === "function" ? receiver === state : state.has(receiver);
990+
};`
991+
};
992+
964993
let allUnscopedEmitHelpers: ReadonlyESMap<string, UnscopedEmitHelper> | undefined;
965994

966995
export function getAllUnscopedEmitHelpers() {
@@ -986,6 +1015,7 @@ namespace ts {
9861015
exportStarHelper,
9871016
classPrivateFieldGetHelper,
9881017
classPrivateFieldSetHelper,
1018+
classPrivateFieldInHelper,
9891019
createBindingHelper,
9901020
setModuleDefaultHelper
9911021
], helper => helper.name));

src/compiler/factory/parenthesizerRules.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -452,4 +452,4 @@ namespace ts {
452452
parenthesizeConstituentTypesOfUnionOrIntersectionType: nodes => cast(nodes, isNodeArray),
453453
parenthesizeTypeArguments: nodes => nodes && cast(nodes, isNodeArray),
454454
};
455-
}
455+
}

src/compiler/parser.ts

+2
Original file line numberDiff line numberDiff line change
@@ -5617,6 +5617,8 @@ namespace ts {
56175617
break;
56185618
case SyntaxKind.TemplateHead:
56195619
return parseTemplateExpression(/* isTaggedTemplate */ false);
5620+
case SyntaxKind.PrivateIdentifier:
5621+
return parsePrivateIdentifier();
56205622
}
56215623

56225624
return parseIdentifier(Diagnostics.Expression_expected);

src/compiler/transformers/classFields.ts

+33-1
Original file line numberDiff line numberDiff line change
@@ -266,15 +266,44 @@ namespace ts {
266266

267267
/**
268268
* If we visit a private name, this means it is an undeclared private name.
269-
* Replace it with an empty identifier to indicate a problem with the code.
269+
* Replace it with an empty identifier to indicate a problem with the code,
270+
* unless we are in a statement position - otherwise this will not trigger
271+
* a SyntaxError.
270272
*/
271273
function visitPrivateIdentifier(node: PrivateIdentifier) {
272274
if (!shouldTransformPrivateElementsOrClassStaticBlocks) {
273275
return node;
274276
}
277+
if (isStatement(node.parent)) {
278+
return node;
279+
}
275280
return setOriginalNode(factory.createIdentifier(""), node);
276281
}
277282

283+
/**
284+
* Visits `#id in expr`
285+
*/
286+
function visitPrivateIdentifierInInExpression(node: BinaryExpression) {
287+
if (!shouldTransformPrivateElementsOrClassStaticBlocks) {
288+
return node;
289+
}
290+
const privId = node.left;
291+
Debug.assertNode(privId, isPrivateIdentifier);
292+
Debug.assert(node.operatorToken.kind === SyntaxKind.InKeyword);
293+
const info = accessPrivateIdentifier(privId);
294+
if (info) {
295+
const receiver = visitNode(node.right, visitor, isExpression);
296+
297+
return setOriginalNode(
298+
context.getEmitHelperFactory().createClassPrivateFieldInHelper(info.brandCheckIdentifier, receiver),
299+
node
300+
);
301+
}
302+
303+
// Private name has not been declared. Subsequent transformers will handle this error
304+
return visitEachChild(node, visitor, context);
305+
}
306+
278307
/**
279308
* Visits the members of a class that has fields.
280309
*
@@ -827,6 +856,9 @@ namespace ts {
827856
}
828857
}
829858
}
859+
if (node.operatorToken.kind === SyntaxKind.InKeyword && isPrivateIdentifier(node.left)) {
860+
return visitPrivateIdentifierInInExpression(node);
861+
}
830862
return visitEachChild(node, visitor, context);
831863
}
832864

src/compiler/types.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1214,7 +1214,8 @@ namespace ts {
12141214
readonly expression: Expression;
12151215
}
12161216

1217-
export interface PrivateIdentifier extends Node {
1217+
// Typed as a PrimaryExpression due to its presence in BinaryExpressions (#field in expr)
1218+
export interface PrivateIdentifier extends PrimaryExpression {
12181219
readonly kind: SyntaxKind.PrivateIdentifier;
12191220
// escaping not strictly necessary
12201221
// avoids gotchas in transforms and utils
@@ -6854,7 +6855,8 @@ namespace ts {
68546855
MakeTemplateObject = 1 << 18, // __makeTemplateObject (used for constructing template string array objects)
68556856
ClassPrivateFieldGet = 1 << 19, // __classPrivateFieldGet (used by the class private field transformation)
68566857
ClassPrivateFieldSet = 1 << 20, // __classPrivateFieldSet (used by the class private field transformation)
6857-
CreateBinding = 1 << 21, // __createBinding (use by the module transform for (re)exports and namespace imports)
6858+
ClassPrivateFieldIn = 1 << 21, // __classPrivateFieldIn (used by the class private field transformation)
6859+
CreateBinding = 1 << 22, // __createBinding (use by the module transform for (re)exports and namespace imports)
68586860
FirstEmitHelper = Extends,
68596861
LastEmitHelper = CreateBinding,
68606862

src/compiler/utilities.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1949,6 +1949,8 @@ namespace ts {
19491949
node = node.parent;
19501950
}
19511951
return node.parent.kind === SyntaxKind.TypeQuery || isJSDocLinkLike(node.parent) || isJSDocNameReference(node.parent) || isJSDocMemberName(node.parent) || isJSXTagName(node);
1952+
case SyntaxKind.PrivateIdentifier:
1953+
return isBinaryExpression(node.parent) && node.parent.left === node && node.parent.operatorToken.kind === SyntaxKind.InKeyword;
19521954
case SyntaxKind.Identifier:
19531955
if (node.parent.kind === SyntaxKind.TypeQuery || isJSDocLinkLike(node.parent) || isJSDocNameReference(node.parent) || isJSDocMemberName(node.parent) || isJSXTagName(node)) {
19541956
return true;
@@ -3706,6 +3708,7 @@ namespace ts {
37063708
case SyntaxKind.ThisKeyword:
37073709
case SyntaxKind.SuperKeyword:
37083710
case SyntaxKind.Identifier:
3711+
case SyntaxKind.PrivateIdentifier:
37093712
case SyntaxKind.NullKeyword:
37103713
case SyntaxKind.TrueKeyword:
37113714
case SyntaxKind.FalseKeyword:

src/compiler/utilitiesPublic.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,7 @@ namespace ts {
15161516
case SyntaxKind.ClassExpression:
15171517
case SyntaxKind.FunctionExpression:
15181518
case SyntaxKind.Identifier:
1519+
case SyntaxKind.PrivateIdentifier: // technically this is only an Expression if it's in a `#field in expr` BinaryExpression
15191520
case SyntaxKind.RegularExpressionLiteral:
15201521
case SyntaxKind.NumericLiteral:
15211522
case SyntaxKind.BigIntLiteral:

src/services/codefixes/fixForgottenThisPropertyAccess.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace ts.codefix {
44
const didYouMeanStaticMemberCode = Diagnostics.Cannot_find_name_0_Did_you_mean_the_static_member_1_0.code;
55
const errorCodes = [
66
Diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0.code,
7-
Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies.code,
7+
Diagnostics.Private_identifiers_are_only_allowed_in_class_bodies_and_may_only_be_used_as_part_of_a_class_member_declaration_property_access_or_on_the_left_hand_side_of_an_in_expression.code,
88
didYouMeanStaticMemberCode,
99
];
1010
registerCodeFix({
@@ -32,7 +32,7 @@ namespace ts.codefix {
3232

3333
function getInfo(sourceFile: SourceFile, pos: number, diagCode: number): Info | undefined {
3434
const node = getTokenAtPosition(sourceFile, pos);
35-
if (isIdentifier(node)) {
35+
if (isIdentifier(node) || isPrivateIdentifier(node)) {
3636
return { node, className: diagCode === didYouMeanStaticMemberCode ? getContainingClass(node)!.name!.text : undefined };
3737
}
3838
}

0 commit comments

Comments
 (0)