@@ -713,6 +713,7 @@ namespace ts {
713
713
714
714
getLocalTypeParametersOfClassOrInterfaceOrTypeAlias,
715
715
isDeclarationVisible,
716
+ isPropertyAccessible,
716
717
};
717
718
718
719
function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, checkMode: CheckMode): Signature | undefined {
@@ -27284,11 +27285,30 @@ namespace ts {
27284
27285
node: PropertyAccessExpression | QualifiedName | PropertyAccessExpression | VariableDeclaration | ParameterDeclaration | ImportTypeNode | PropertyAssignment | ShorthandPropertyAssignment | BindingElement,
27285
27286
isSuper: boolean, writing: boolean, type: Type, prop: Symbol, reportError = true): boolean {
27286
27287
27287
- const flags = getDeclarationModifierFlagsFromSymbol(prop, writing);
27288
- const errorNode = node.kind === SyntaxKind.QualifiedName ? node.right :
27288
+ const errorNode = !reportError ? undefined :
27289
+ node.kind === SyntaxKind.QualifiedName ? node.right :
27289
27290
node.kind === SyntaxKind.ImportType ? node :
27290
27291
node.kind === SyntaxKind.BindingElement && node.propertyName ? node.propertyName : node.name;
27291
27292
27293
+ return checkPropertyAccessibilityAtLocation(node, isSuper, writing, type, prop, errorNode);
27294
+ }
27295
+
27296
+ /**
27297
+ * Check whether the requested property can be accessed at the requested location.
27298
+ * Returns true if node is a valid property access, and false otherwise.
27299
+ * @param location The location node where we want to check if the property is accessible.
27300
+ * @param isSuper True if the access is from `super.`.
27301
+ * @param writing True if this is a write property access, false if it is a read property access.
27302
+ * @param containingType The type of the object whose property is being accessed. (Not the type of the property.)
27303
+ * @param prop The symbol for the property being accessed.
27304
+ * @param errorNode The node where we should report an invalid property access error, or undefined if we should not report errors.
27305
+ */
27306
+ function checkPropertyAccessibilityAtLocation(location: Node,
27307
+ isSuper: boolean, writing: boolean,
27308
+ containingType: Type, prop: Symbol, errorNode?: Node): boolean {
27309
+
27310
+ const flags = getDeclarationModifierFlagsFromSymbol(prop, writing);
27311
+
27292
27312
if (isSuper) {
27293
27313
// TS 1.0 spec (April 2014): 4.8.2
27294
27314
// - In a constructor, instance member function, instance member accessor, or
@@ -27299,7 +27319,7 @@ namespace ts {
27299
27319
// a super property access is permitted and must specify a public static member function of the base class.
27300
27320
if (languageVersion < ScriptTarget.ES2015) {
27301
27321
if (symbolHasNonMethodDeclaration(prop)) {
27302
- if (reportError ) {
27322
+ if (errorNode ) {
27303
27323
error(errorNode, Diagnostics.Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword);
27304
27324
}
27305
27325
return false;
@@ -27310,20 +27330,26 @@ namespace ts {
27310
27330
// This error could mask a private property access error. But, a member
27311
27331
// cannot simultaneously be private and abstract, so this will trigger an
27312
27332
// additional error elsewhere.
27313
- if (reportError) {
27314
- error(errorNode, Diagnostics.Abstract_method_0_in_class_1_cannot_be_accessed_via_super_expression, symbolToString(prop), typeToString(getDeclaringClass(prop)!));
27333
+ if (errorNode) {
27334
+ error(errorNode,
27335
+ Diagnostics.Abstract_method_0_in_class_1_cannot_be_accessed_via_super_expression,
27336
+ symbolToString(prop),
27337
+ typeToString(getDeclaringClass(prop)!));
27315
27338
}
27316
27339
return false;
27317
27340
}
27318
27341
}
27319
27342
27320
27343
// Referencing abstract properties within their own constructors is not allowed
27321
27344
if ((flags & ModifierFlags.Abstract) && symbolHasNonMethodDeclaration(prop) &&
27322
- (isThisProperty(node ) || isThisInitializedObjectBindingExpression(node ) || isObjectBindingPattern(node .parent) && isThisInitializedDeclaration(node .parent.parent))) {
27345
+ (isThisProperty(location ) || isThisInitializedObjectBindingExpression(location ) || isObjectBindingPattern(location .parent) && isThisInitializedDeclaration(location .parent.parent))) {
27323
27346
const declaringClassDeclaration = getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop)!);
27324
- if (declaringClassDeclaration && isNodeUsedDuringClassInitialization(node)) {
27325
- if (reportError) {
27326
- error(errorNode, Diagnostics.Abstract_property_0_in_class_1_cannot_be_accessed_in_the_constructor, symbolToString(prop), getTextOfIdentifierOrLiteral(declaringClassDeclaration.name!)); // TODO: GH#18217
27347
+ if (declaringClassDeclaration && isNodeUsedDuringClassInitialization(location)) {
27348
+ if (errorNode) {
27349
+ error(errorNode,
27350
+ Diagnostics.Abstract_property_0_in_class_1_cannot_be_accessed_in_the_constructor,
27351
+ symbolToString(prop),
27352
+ getTextOfIdentifierOrLiteral(declaringClassDeclaration.name!));
27327
27353
}
27328
27354
return false;
27329
27355
}
@@ -27339,9 +27365,12 @@ namespace ts {
27339
27365
// Private property is accessible if the property is within the declaring class
27340
27366
if (flags & ModifierFlags.Private) {
27341
27367
const declaringClassDeclaration = getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop)!)!;
27342
- if (!isNodeWithinClass(node, declaringClassDeclaration)) {
27343
- if (reportError) {
27344
- error(errorNode, Diagnostics.Property_0_is_private_and_only_accessible_within_class_1, symbolToString(prop), typeToString(getDeclaringClass(prop)!));
27368
+ if (!isNodeWithinClass(location, declaringClassDeclaration)) {
27369
+ if (errorNode) {
27370
+ error(errorNode,
27371
+ Diagnostics.Property_0_is_private_and_only_accessible_within_class_1,
27372
+ symbolToString(prop),
27373
+ typeToString(getDeclaringClass(prop)!));
27345
27374
}
27346
27375
return false;
27347
27376
}
@@ -27357,7 +27386,7 @@ namespace ts {
27357
27386
27358
27387
// Find the first enclosing class that has the declaring classes of the protected constituents
27359
27388
// of the property as base classes
27360
- let enclosingClass = forEachEnclosingClass(node , enclosingDeclaration => {
27389
+ let enclosingClass = forEachEnclosingClass(location , enclosingDeclaration => {
27361
27390
const enclosingClass = getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingDeclaration)!) as InterfaceType;
27362
27391
return isClassDerivedFromDeclaringClasses(enclosingClass, prop, writing) ? enclosingClass : undefined;
27363
27392
});
@@ -27366,9 +27395,12 @@ namespace ts {
27366
27395
// allow PropertyAccessibility if context is in function with this parameter
27367
27396
// static member access is disallow
27368
27397
let thisParameter: ParameterDeclaration | undefined;
27369
- if (flags & ModifierFlags.Static || !(thisParameter = getThisParameterFromNodeContext(node)) || !thisParameter.type) {
27370
- if (reportError) {
27371
- error(errorNode, Diagnostics.Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses, symbolToString(prop), typeToString(getDeclaringClass(prop) || type));
27398
+ if (flags & ModifierFlags.Static || !(thisParameter = getThisParameterFromNodeContext(location)) || !thisParameter.type) {
27399
+ if (errorNode) {
27400
+ error(errorNode,
27401
+ Diagnostics.Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses,
27402
+ symbolToString(prop),
27403
+ typeToString(getDeclaringClass(prop) || containingType));
27372
27404
}
27373
27405
return false;
27374
27406
}
@@ -27380,13 +27412,15 @@ namespace ts {
27380
27412
if (flags & ModifierFlags.Static) {
27381
27413
return true;
27382
27414
}
27383
- if (type .flags & TypeFlags.TypeParameter) {
27415
+ if (containingType .flags & TypeFlags.TypeParameter) {
27384
27416
// get the original type -- represented as the type constraint of the 'this' type
27385
- type = (type as TypeParameter).isThisType ? getConstraintOfTypeParameter(type as TypeParameter)! : getBaseConstraintOfType(type as TypeParameter)!; // TODO: GH#18217 Use a different variable that's allowed to be undefined
27417
+ containingType = (containingType as TypeParameter).isThisType ? getConstraintOfTypeParameter(containingType as TypeParameter)! : getBaseConstraintOfType(containingType as TypeParameter)!; // TODO: GH#18217 Use a different variable that's allowed to be undefined
27386
27418
}
27387
- if (!type || !hasBaseType(type, enclosingClass)) {
27388
- if (reportError) {
27389
- error(errorNode, Diagnostics.Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1_This_is_an_instance_of_class_2, symbolToString(prop), typeToString(enclosingClass), typeToString(type));
27419
+ if (!containingType || !hasBaseType(containingType, enclosingClass)) {
27420
+ if (errorNode) {
27421
+ error(errorNode,
27422
+ Diagnostics.Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1_This_is_an_instance_of_class_2,
27423
+ symbolToString(prop), typeToString(enclosingClass), typeToString(containingType));
27390
27424
}
27391
27425
return false;
27392
27426
}
@@ -28110,8 +28144,22 @@ namespace ts {
28110
28144
}
28111
28145
}
28112
28146
28147
+ /**
28148
+ * Checks if an existing property access is valid for completions purposes.
28149
+ * @param node a property access-like node where we want to check if we can access a property.
28150
+ * This node does not need to be an access of the property we are checking.
28151
+ * e.g. in completions, this node will often be an incomplete property access node, as in `foo.`.
28152
+ * Besides providing a location (i.e. scope) used to check property accessibility, we use this node for
28153
+ * computing whether this is a `super` property access.
28154
+ * @param type the type whose property we are checking.
28155
+ * @param property the accessed property's symbol.
28156
+ */
28113
28157
function isValidPropertyAccessForCompletions(node: PropertyAccessExpression | ImportTypeNode | QualifiedName, type: Type, property: Symbol): boolean {
28114
- return isValidPropertyAccessWithType(node, node.kind === SyntaxKind.PropertyAccessExpression && node.expression.kind === SyntaxKind.SuperKeyword, property.escapedName, type);
28158
+ return isPropertyAccessible(node,
28159
+ node.kind === SyntaxKind.PropertyAccessExpression && node.expression.kind === SyntaxKind.SuperKeyword,
28160
+ /* isWrite */ false,
28161
+ type,
28162
+ property);
28115
28163
// Previously we validated the 'this' type of methods but this adversely affected performance. See #31377 for more context.
28116
28164
}
28117
28165
@@ -28121,19 +28169,45 @@ namespace ts {
28121
28169
propertyName: __String,
28122
28170
type: Type): boolean {
28123
28171
28172
+ // Short-circuiting for improved performance.
28124
28173
if (type === errorType || isTypeAny(type)) {
28125
28174
return true;
28126
28175
}
28176
+
28127
28177
const prop = getPropertyOfType(type, propertyName);
28128
- if (prop) {
28129
- if (prop.valueDeclaration && isPrivateIdentifierClassElementDeclaration(prop.valueDeclaration)) {
28130
- const declClass = getContainingClass(prop.valueDeclaration);
28131
- return !isOptionalChain(node) && !!findAncestor(node, parent => parent === declClass);
28132
- }
28133
- return checkPropertyAccessibility(node, isSuper, /*writing*/ false, type, prop, /* reportError */ false);
28178
+ return !!prop && isPropertyAccessible(node, isSuper, /* isWrite */ false, type, prop);
28179
+ }
28180
+
28181
+ /**
28182
+ * Checks if a property can be accessed in a location.
28183
+ * The location is given by the `node` parameter.
28184
+ * The node does not need to be a property access.
28185
+ * @param node location where to check property accessibility
28186
+ * @param isSuper whether to consider this a `super` property access, e.g. `super.foo`.
28187
+ * @param isWrite whether this is a write access, e.g. `++foo.x`.
28188
+ * @param containingType type where the property comes from.
28189
+ * @param property property symbol.
28190
+ */
28191
+ function isPropertyAccessible(
28192
+ node: Node,
28193
+ isSuper: boolean,
28194
+ isWrite: boolean,
28195
+ containingType: Type,
28196
+ property: Symbol): boolean {
28197
+
28198
+ // Short-circuiting for improved performance.
28199
+ if (containingType === errorType || isTypeAny(containingType)) {
28200
+ return true;
28201
+ }
28202
+
28203
+ // A #private property access in an optional chain is an error dealt with by the parser.
28204
+ // The checker does not check for it, so we need to do our own check here.
28205
+ if (property.valueDeclaration && isPrivateIdentifierClassElementDeclaration(property.valueDeclaration)) {
28206
+ const declClass = getContainingClass(property.valueDeclaration);
28207
+ return !isOptionalChain(node) && !!findAncestor(node, parent => parent === declClass);
28134
28208
}
28135
- // In js files properties of unions are allowed in completion
28136
- return isInJSFile (node) && (type.flags & TypeFlags.Union) !== 0 && (type as UnionType).types.some(elementType => isValidPropertyAccessWithType(node , isSuper, propertyName, elementType) );
28209
+
28210
+ return checkPropertyAccessibilityAtLocation (node, isSuper, isWrite, containingType, property );
28137
28211
}
28138
28212
28139
28213
/**
0 commit comments