@@ -132,6 +132,7 @@ namespace ts {
132
132
getDeclaredTypeOfSymbol,
133
133
getPropertiesOfType,
134
134
getPropertyOfType: (type, name) => getPropertyOfType(type, escapeLeadingUnderscores(name)),
135
+ getPropertyForPrivateName,
135
136
getTypeOfPropertyOfType: (type, name) => getTypeOfPropertyOfType(type, escapeLeadingUnderscores(name)),
136
137
getIndexInfoOfType,
137
138
getSignaturesOfType,
@@ -1606,8 +1607,8 @@ namespace ts {
1606
1607
}
1607
1608
}
1608
1609
1609
- function diagnosticName(nameArg: __String | Identifier) {
1610
- return isString(nameArg) ? unescapeLeadingUnderscores(nameArg as __String) : declarationNameToString(nameArg as Identifier);
1610
+ function diagnosticName(nameArg: __String | Identifier | PrivateName ) {
1611
+ return isString(nameArg) ? unescapeLeadingUnderscores(nameArg as __String) : declarationNameToString(nameArg as Identifier | PrivateName );
1611
1612
}
1612
1613
1613
1614
function isTypeParameterSymbolDeclaredInContainer(symbol: Symbol, container: Node) {
@@ -2796,15 +2797,16 @@ namespace ts {
2796
2797
return type;
2797
2798
}
2798
2799
2799
- // A reserved member name starts with two underscores, but the third character cannot be an underscore
2800
- // or the @ symbol . A third underscore indicates an escaped form of an identifer that started
2800
+ // A reserved member name starts with two underscores, but the third character cannot be an underscore,
2801
+ // @, or # . A third underscore indicates an escaped form of an identifer that started
2801
2802
// with at least two underscores. The @ character indicates that the name is denoted by a well known ES
2802
- // Symbol instance.
2803
+ // Symbol instance and the # indicates that the name is a PrivateName .
2803
2804
function isReservedMemberName(name: __String) {
2804
2805
return (name as string).charCodeAt(0) === CharacterCodes._ &&
2805
2806
(name as string).charCodeAt(1) === CharacterCodes._ &&
2806
2807
(name as string).charCodeAt(2) !== CharacterCodes._ &&
2807
- (name as string).charCodeAt(2) !== CharacterCodes.at;
2808
+ (name as string).charCodeAt(2) !== CharacterCodes.at &&
2809
+ (name as string).charCodeAt(2) !== CharacterCodes.hash;
2808
2810
}
2809
2811
2810
2812
function getNamedMembers(members: SymbolTable): Symbol[] {
@@ -6511,7 +6513,7 @@ namespace ts {
6511
6513
*/
6512
6514
function getPropertyNameFromType(type: StringLiteralType | NumberLiteralType | UniqueESSymbolType): __String {
6513
6515
if (type.flags & TypeFlags.UniqueESSymbol) {
6514
- return (<UniqueESSymbolType> type).escapedName ;
6516
+ return getPropertyNameForUniqueESSymbol( type.symbol) ;
6515
6517
}
6516
6518
if (type.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) {
6517
6519
return escapeLeadingUnderscores("" + (<StringLiteralType | NumberLiteralType>type).value);
@@ -9694,6 +9696,9 @@ namespace ts {
9694
9696
}
9695
9697
9696
9698
function getLiteralTypeFromPropertyName(name: PropertyName) {
9699
+ if (isPrivateName(name)) {
9700
+ return neverType;
9701
+ }
9697
9702
return isIdentifier(name) ? getLiteralType(unescapeLeadingUnderscores(name.escapedText)) :
9698
9703
getRegularTypeOfLiteralType(isComputedPropertyName(name) ? checkComputedPropertyName(name) : checkExpression(name));
9699
9704
}
@@ -12929,23 +12934,44 @@ namespace ts {
12929
12934
const unmatchedProperty = getUnmatchedProperty(source, target, requireOptionalProperties, /*matchDiscriminantProperties*/ false);
12930
12935
if (unmatchedProperty) {
12931
12936
if (reportErrors) {
12932
- const props = arrayFrom(getUnmatchedProperties(source, target, requireOptionalProperties, /*matchDiscriminantProperties*/ false));
12933
- if (!headMessage || (headMessage.code !== Diagnostics.Class_0_incorrectly_implements_interface_1.code &&
12934
- headMessage.code !== Diagnostics.Class_0_incorrectly_implements_class_1_Did_you_mean_to_extend_1_and_inherit_its_members_as_a_subclass.code)) {
12935
- suppressNextError = true; // Retain top-level error for interface implementing issues, otherwise omit it
12936
- }
12937
- if (props.length === 1) {
12938
- const propName = symbolToString(unmatchedProperty);
12939
- reportError(Diagnostics.Property_0_is_missing_in_type_1_but_required_in_type_2, propName, typeToString(source), typeToString(target));
12940
- if (length(unmatchedProperty.declarations)) {
12941
- associateRelatedInfo(createDiagnosticForNode(unmatchedProperty.declarations[0], Diagnostics._0_is_declared_here, propName));
12937
+ let hasReported = false;
12938
+ // give specific error in case where private names have the same description
12939
+ if (
12940
+ unmatchedProperty.valueDeclaration
12941
+ && isNamedDeclaration(unmatchedProperty.valueDeclaration)
12942
+ && isPrivateName(unmatchedProperty.valueDeclaration.name)
12943
+ && isClassDeclaration(source.symbol.valueDeclaration)
12944
+ ) {
12945
+ const privateNameDescription = unmatchedProperty.valueDeclaration.name.escapedText;
12946
+ const symbolTableKey = getPropertyNameForPrivateNameDescription(source.symbol, privateNameDescription);
12947
+ if (symbolTableKey && !!getPropertyOfType(source, symbolTableKey)) {
12948
+ reportError(
12949
+ Diagnostics.Property_0_is_missing_in_type_1_While_type_1_has_a_private_member_with_the_same_spelling_its_declaration_and_accessibility_are_distinct,
12950
+ diagnosticName(privateNameDescription),
12951
+ diagnosticName(source.symbol.valueDeclaration.name || ("(anonymous)" as __String))
12952
+ );
12953
+ hasReported = true;
12942
12954
}
12943
12955
}
12944
- else if (props.length > 5) { // arbitrary cutoff for too-long list form
12945
- reportError(Diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2_and_3_more, typeToString(source), typeToString(target), map(props.slice(0, 4), p => symbolToString(p)).join(", "), props.length - 4);
12946
- }
12947
- else {
12948
- reportError(Diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2, typeToString(source), typeToString(target), map(props, p => symbolToString(p)).join(", "));
12956
+ if (!hasReported) {
12957
+ const props = arrayFrom(getUnmatchedProperties(source, target, requireOptionalProperties, /*matchDiscriminantProperties*/ false));
12958
+ if (!headMessage || (headMessage.code !== Diagnostics.Class_0_incorrectly_implements_interface_1.code &&
12959
+ headMessage.code !== Diagnostics.Class_0_incorrectly_implements_class_1_Did_you_mean_to_extend_1_and_inherit_its_members_as_a_subclass.code)) {
12960
+ suppressNextError = true; // Retain top-level error for interface implementing issues, otherwise omit it
12961
+ }
12962
+ if (props.length === 1) {
12963
+ const propName = symbolToString(unmatchedProperty);
12964
+ reportError(Diagnostics.Property_0_is_missing_in_type_1_but_required_in_type_2, propName, typeToString(source), typeToString(target));
12965
+ if (length(unmatchedProperty.declarations)) {
12966
+ associateRelatedInfo(createDiagnosticForNode(unmatchedProperty.declarations[0], Diagnostics._0_is_declared_here, propName));
12967
+ }
12968
+ }
12969
+ else if (props.length > 5) { // arbitrary cutoff for too-long list form
12970
+ reportError(Diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2_and_3_more, typeToString(source), typeToString(target), map(props.slice(0, 4), p => symbolToString(p)).join(", "), props.length - 4);
12971
+ }
12972
+ else {
12973
+ reportError(Diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2, typeToString(source), typeToString(target), map(props, p => symbolToString(p)).join(", "));
12974
+ }
12949
12975
}
12950
12976
}
12951
12977
return Ternary.False;
@@ -19383,6 +19409,48 @@ namespace ts {
19383
19409
return checkPropertyAccessExpressionOrQualifiedName(node, node.left, node.right);
19384
19410
}
19385
19411
19412
+ function getPropertyForPrivateName(apparentType: Type, leftType: Type, right: PrivateName, errorNode: Node | undefined): Symbol | undefined {
19413
+ let classWithShadowedPrivateName;
19414
+ let container = getContainingClass(right);
19415
+ while (container) {
19416
+ const symbolTableKey = getPropertyNameForPrivateNameDescription(container.symbol, right.escapedText);
19417
+ if (symbolTableKey) {
19418
+ const prop = getPropertyOfType(apparentType, symbolTableKey);
19419
+ if (prop) {
19420
+ if (classWithShadowedPrivateName) {
19421
+ if (errorNode) {
19422
+ error(
19423
+ errorNode,
19424
+ Diagnostics.This_usage_of_0_refers_to_the_private_member_declared_in_its_enclosing_class_While_type_1_has_a_private_member_with_the_same_spelling_its_declaration_and_accessibility_are_distinct,
19425
+ diagnosticName(right),
19426
+ diagnosticName(classWithShadowedPrivateName.name || ("(anonymous)" as __String))
19427
+ );
19428
+ }
19429
+ return undefined;
19430
+ }
19431
+ return prop;
19432
+ }
19433
+ else {
19434
+ classWithShadowedPrivateName = container;
19435
+ }
19436
+ }
19437
+ container = getContainingClass(container);
19438
+ }
19439
+ // If this isn't a case of shadowing, and the lhs has a property with the same
19440
+ // private name description, then there is a privacy violation
19441
+ if (leftType.symbol.members) {
19442
+ const symbolTableKey = getPropertyNameForPrivateNameDescription(leftType.symbol, right.escapedText);
19443
+ const prop = getPropertyOfType(apparentType, symbolTableKey);
19444
+ if (prop) {
19445
+ if (errorNode) {
19446
+ error(right, Diagnostics.Property_0_is_not_accessible_outside_class_1_because_it_has_a_private_name, symbolToString(prop), typeToString(getDeclaringClass(prop)!));
19447
+ }
19448
+ }
19449
+ }
19450
+ // not found
19451
+ return undefined;
19452
+ }
19453
+
19386
19454
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier | PrivateName) {
19387
19455
let propType: Type;
19388
19456
const leftType = checkNonNullExpression(left);
@@ -19395,7 +19463,7 @@ namespace ts {
19395
19463
return apparentType;
19396
19464
}
19397
19465
const assignmentKind = getAssignmentTargetKind(node);
19398
- const prop = getPropertyOfType(apparentType, right.escapedText);
19466
+ const prop = isPrivateName(right) ? getPropertyForPrivateName(apparentType, leftType, right, /* errorNode */ right) : getPropertyOfType(apparentType, right.escapedText);
19399
19467
if (isIdentifier(left) && parentSymbol && !(prop && isConstEnumOrConstEnumOnlyModule(prop))) {
19400
19468
markAliasReferenced(parentSymbol, node);
19401
19469
}
@@ -22384,6 +22452,9 @@ namespace ts {
22384
22452
error(expr, Diagnostics.The_operand_of_a_delete_operator_must_be_a_property_reference);
22385
22453
return booleanType;
22386
22454
}
22455
+ if (expr.kind === SyntaxKind.PropertyAccessExpression && isPrivateName((expr as PropertyAccessExpression).name)) {
22456
+ error(expr, Diagnostics.The_operand_of_a_delete_operator_cannot_be_a_private_name);
22457
+ }
22387
22458
const links = getNodeLinks(expr);
22388
22459
const symbol = getExportSymbolOfValueSymbolIfExported(links.resolvedSymbol);
22389
22460
if (symbol && isReadonlySymbol(symbol)) {
@@ -23592,9 +23663,6 @@ namespace ts {
23592
23663
checkGrammarDecoratorsAndModifiers(node);
23593
23664
23594
23665
checkVariableLikeDeclaration(node);
23595
- if (node.name && isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
23596
- error(node, Diagnostics.Private_names_cannot_be_used_as_parameters);
23597
- }
23598
23666
const func = getContainingFunction(node)!;
23599
23667
if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) {
23600
23668
if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) {
@@ -30350,6 +30418,9 @@ namespace ts {
30350
30418
else if (node.kind === SyntaxKind.Parameter && (flags & ModifierFlags.ParameterPropertyModifier) && (<ParameterDeclaration>node).dotDotDotToken) {
30351
30419
return grammarErrorOnNode(node, Diagnostics.A_parameter_property_cannot_be_declared_using_a_rest_parameter);
30352
30420
}
30421
+ else if (isNamedDeclaration(node) && (flags & ModifierFlags.AccessibilityModifier) && node.name.kind === SyntaxKind.PrivateName) {
30422
+ return grammarErrorOnNode(node, Diagnostics.Accessibility_modifiers_cannot_be_used_with_private_names);
30423
+ }
30353
30424
if (flags & ModifierFlags.Async) {
30354
30425
return checkGrammarAsyncModifier(node, lastAsync!);
30355
30426
}
@@ -30747,6 +30818,10 @@ namespace ts {
30747
30818
return grammarErrorOnNode(prop.equalsToken!, Diagnostics.can_only_be_used_in_an_object_literal_property_inside_a_destructuring_assignment);
30748
30819
}
30749
30820
30821
+ if (name.kind === SyntaxKind.PrivateName) {
30822
+ return grammarErrorOnNode(name, Diagnostics.Private_names_are_not_allowed_outside_class_bodies);
30823
+ }
30824
+
30750
30825
// Modifiers are never allowed on properties except for 'async' on a method declaration
30751
30826
if (prop.modifiers) {
30752
30827
for (const mod of prop.modifiers!) { // TODO: GH#19955
@@ -31188,10 +31263,6 @@ namespace ts {
31188
31263
checkESModuleMarker(node.name);
31189
31264
}
31190
31265
31191
- if (isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
31192
- return grammarErrorOnNode(node.name, Diagnostics.Private_names_are_not_allowed_in_variable_declarations);
31193
- }
31194
-
31195
31266
const checkLetConstNames = (isLet(node) || isVarConst(node));
31196
31267
31197
31268
// 1. LexicalDeclaration : LetOrConst BindingList ;
0 commit comments