@@ -263,30 +263,6 @@ namespace ts {
263
263
Uncapitalize: IntrinsicTypeKind.Uncapitalize
264
264
}));
265
265
266
- enum TypeChecks {
267
- Ok,
268
- HasDiagnostic
269
- }
270
-
271
- interface CheckerResultOk {
272
- result: TypeChecks.Ok
273
- }
274
-
275
- interface CheckerResultHasDiagnostic {
276
- result: TypeChecks.HasDiagnostic,
277
- message: DiagnosticMessage;
278
- args:
279
- | never[]
280
- | [string | number]
281
- | [string | number, string | number]
282
- | [string | number, string | number, string | number]
283
- | [string | number, string | number, string | number, string | number];
284
- }
285
-
286
- type CheckerResult =
287
- | CheckerResultOk
288
- | CheckerResultHasDiagnostic;
289
-
290
266
function SymbolLinks(this: SymbolLinks) {
291
267
}
292
268
@@ -736,9 +712,7 @@ namespace ts {
736
712
737
713
getLocalTypeParametersOfClassOrInterfaceOrTypeAlias,
738
714
isDeclarationVisible,
739
- isPropertyAccessible: (node, isSuper, writing, type, prop) => {
740
- return checkPropertyAccessibilityAtNode(node, isSuper, writing, type, prop).result === TypeChecks.Ok;
741
- }
715
+ isPropertyAccessible,
742
716
};
743
717
744
718
function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, checkMode: CheckMode): Signature | undefined {
@@ -27293,35 +27267,28 @@ namespace ts {
27293
27267
node: PropertyAccessExpression | QualifiedName | PropertyAccessExpression | VariableDeclaration | ParameterDeclaration | ImportTypeNode | PropertyAssignment | ShorthandPropertyAssignment | BindingElement,
27294
27268
isSuper: boolean, writing: boolean, type: Type, prop: Symbol, reportError = true): boolean {
27295
27269
27296
- const errorNode = node.kind === SyntaxKind.QualifiedName ? node.right :
27270
+ const errorNode = !reportError ? undefined : node.kind === SyntaxKind.QualifiedName ? node.right :
27297
27271
node.kind === SyntaxKind.ImportType ? node :
27298
27272
node.kind === SyntaxKind.BindingElement && node.propertyName ? node.propertyName : node.name;
27299
27273
27300
- const typeChecks = checkPropertyAccessibilityAtNode(node, isSuper, writing, type, prop);
27301
- switch (typeChecks.result) {
27302
- case (TypeChecks.Ok):
27303
- return true;
27304
- case (TypeChecks.HasDiagnostic):
27305
- if (reportError) {
27306
- error(errorNode, typeChecks.message, ...typeChecks.args);
27307
- }
27308
- return false;
27309
- }
27274
+ return checkPropertyAccessibilityAtLocation(node, isSuper, writing, type, prop, errorNode);
27310
27275
}
27311
27276
27312
- // Possible concerns:
27313
- // (1) I'm not sure if this function has the right behavior if `node` is allowed to be any node,
27314
- // although we only use `node` for its location in the parse tree.
27315
- // (2) Does it even make sense to check for property accessibility at a certain arbitrary node?
27316
- // Maybe this function should be called "checkPropertyVisibilityAtNode" or something else.
27317
- function checkPropertyAccessibilityAtNode(node: Node,
27277
+ /**
27278
+ * Check whether the requested property can be accessed at the requested location.
27279
+ * Returns true if node is a valid property access, and false otherwise.
27280
+ * @param location The location node where we want to check if the property is accessible.
27281
+ * @param isSuper True if the access is from `super.`.
27282
+ * @param writing True if this is a write property access, false if it is a read property access.
27283
+ * @param type The type of the object whose property is being accessed. (Not the type of the property.)
27284
+ * @param prop The symbol for the property being accessed.
27285
+ * @param errorNode The node where we should report an invalid property access error, or undefined if we should not report errors.
27286
+ */
27287
+ function checkPropertyAccessibilityAtLocation(location: Node,
27318
27288
isSuper: boolean, writing: boolean,
27319
- type: Type, prop: Symbol): CheckerResult {
27320
- const flags = getDeclarationModifierFlagsFromSymbol(prop, writing);
27289
+ type: Type, prop: Symbol, errorNode?: Node): boolean {
27321
27290
27322
- const checkerOk: CheckerResultOk = {
27323
- result: TypeChecks.Ok
27324
- };
27291
+ const flags = getDeclarationModifierFlagsFromSymbol(prop, writing);
27325
27292
27326
27293
if (isSuper) {
27327
27294
// TS 1.0 spec (April 2014): 4.8.2
@@ -27333,69 +27300,74 @@ namespace ts {
27333
27300
// a super property access is permitted and must specify a public static member function of the base class.
27334
27301
if (languageVersion < ScriptTarget.ES2015) {
27335
27302
if (symbolHasNonMethodDeclaration(prop)) {
27336
- return {
27337
- result: TypeChecks.HasDiagnostic,
27338
- message: Diagnostics.Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword,
27339
- args: emptyArray
27340
- };
27303
+ if (errorNode) {
27304
+ error(errorNode, Diagnostics.Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword);
27305
+ }
27306
+ return false;
27341
27307
}
27342
27308
}
27343
27309
if (flags & ModifierFlags.Abstract) {
27344
27310
// A method cannot be accessed in a super property access if the method is abstract.
27345
27311
// This error could mask a private property access error. But, a member
27346
27312
// cannot simultaneously be private and abstract, so this will trigger an
27347
27313
// additional error elsewhere.
27348
- return {
27349
- result: TypeChecks.HasDiagnostic,
27350
- message: Diagnostics.Abstract_method_0_in_class_1_cannot_be_accessed_via_super_expression,
27351
- args: [symbolToString(prop), typeToString(getDeclaringClass(prop)!)]
27352
- };
27314
+ if (errorNode) {
27315
+ error(errorNode,
27316
+ Diagnostics.Abstract_method_0_in_class_1_cannot_be_accessed_via_super_expression,
27317
+ symbolToString(prop),
27318
+ typeToString(getDeclaringClass(prop)!));
27319
+ }
27320
+ return false;
27353
27321
}
27354
27322
}
27355
27323
27356
27324
// Referencing abstract properties within their own constructors is not allowed
27357
27325
if ((flags & ModifierFlags.Abstract) && symbolHasNonMethodDeclaration(prop) &&
27358
- (isThisProperty(node ) || isThisInitializedObjectBindingExpression(node ) || isObjectBindingPattern(node .parent) && isThisInitializedDeclaration(node .parent.parent))) {
27326
+ (isThisProperty(location ) || isThisInitializedObjectBindingExpression(location ) || isObjectBindingPattern(location .parent) && isThisInitializedDeclaration(location .parent.parent))) {
27359
27327
const declaringClassDeclaration = getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop)!);
27360
- if (declaringClassDeclaration && isNodeUsedDuringClassInitialization(node)) {
27361
- return {
27362
- result: TypeChecks.HasDiagnostic,
27363
- message: Diagnostics.Abstract_property_0_in_class_1_cannot_be_accessed_in_the_constructor,
27364
- args: [symbolToString(prop), getTextOfIdentifierOrLiteral(declaringClassDeclaration.name!)]
27365
- };
27328
+ if (declaringClassDeclaration && isNodeUsedDuringClassInitialization(location)) {
27329
+ if (errorNode) {
27330
+ error(errorNode,
27331
+ Diagnostics.Abstract_property_0_in_class_1_cannot_be_accessed_in_the_constructor,
27332
+ symbolToString(prop),
27333
+ getTextOfIdentifierOrLiteral(declaringClassDeclaration.name!));
27334
+ }
27335
+ return false;
27366
27336
}
27367
27337
}
27368
27338
27369
27339
// Public properties are otherwise accessible.
27370
27340
if (!(flags & ModifierFlags.NonPublicAccessibilityModifier)) {
27371
- return checkerOk ;
27341
+ return true ;
27372
27342
}
27373
27343
27374
27344
// Property is known to be private or protected at this point
27375
27345
27376
27346
// Private property is accessible if the property is within the declaring class
27377
27347
if (flags & ModifierFlags.Private) {
27378
27348
const declaringClassDeclaration = getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop)!)!;
27379
- if (!isNodeWithinClass(node, declaringClassDeclaration)) {
27380
- return {
27381
- result: TypeChecks.HasDiagnostic,
27382
- message: Diagnostics.Property_0_is_private_and_only_accessible_within_class_1,
27383
- args: [symbolToString(prop), typeToString(getDeclaringClass(prop)!)]
27384
- };
27349
+ if (!isNodeWithinClass(location, declaringClassDeclaration)) {
27350
+ if (errorNode) {
27351
+ error(errorNode,
27352
+ Diagnostics.Property_0_is_private_and_only_accessible_within_class_1,
27353
+ symbolToString(prop),
27354
+ typeToString(getDeclaringClass(prop)!));
27355
+ }
27356
+ return false;
27385
27357
}
27386
- return checkerOk ;
27358
+ return true ;
27387
27359
}
27388
27360
27389
27361
// Property is known to be protected at this point
27390
27362
27391
27363
// All protected properties of a supertype are accessible in a super access
27392
27364
if (isSuper) {
27393
- return checkerOk ;
27365
+ return true ;
27394
27366
}
27395
27367
27396
27368
// Find the first enclosing class that has the declaring classes of the protected constituents
27397
27369
// of the property as base classes
27398
- let enclosingClass = forEachEnclosingClass(node , enclosingDeclaration => {
27370
+ let enclosingClass = forEachEnclosingClass(location , enclosingDeclaration => {
27399
27371
const enclosingClass = getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingDeclaration)!) as InterfaceType;
27400
27372
return isClassDerivedFromDeclaringClasses(enclosingClass, prop, writing) ? enclosingClass : undefined;
27401
27373
});
@@ -27404,33 +27376,36 @@ namespace ts {
27404
27376
// allow PropertyAccessibility if context is in function with this parameter
27405
27377
// static member access is disallow
27406
27378
let thisParameter: ParameterDeclaration | undefined;
27407
- if (flags & ModifierFlags.Static || !(thisParameter = getThisParameterFromNodeContext(node)) || !thisParameter.type) {
27408
- return {
27409
- result: TypeChecks.HasDiagnostic,
27410
- message: Diagnostics.Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses,
27411
- args: [symbolToString(prop), typeToString(getDeclaringClass(prop) || type)]
27412
- };
27379
+ if (flags & ModifierFlags.Static || !(thisParameter = getThisParameterFromNodeContext(location)) || !thisParameter.type) {
27380
+ if (errorNode) {
27381
+ error(errorNode,
27382
+ Diagnostics.Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses,
27383
+ symbolToString(prop),
27384
+ typeToString(getDeclaringClass(prop) || type));
27385
+ }
27386
+ return false;
27413
27387
}
27414
27388
27415
27389
const thisType = getTypeFromTypeNode(thisParameter.type);
27416
27390
enclosingClass = (((thisType.flags & TypeFlags.TypeParameter) ? getConstraintOfTypeParameter(thisType as TypeParameter) : thisType) as TypeReference).target;
27417
27391
}
27418
27392
// No further restrictions for static properties
27419
27393
if (flags & ModifierFlags.Static) {
27420
- return checkerOk ;
27394
+ return true ;
27421
27395
}
27422
27396
if (type.flags & TypeFlags.TypeParameter) {
27423
27397
// get the original type -- represented as the type constraint of the 'this' type
27424
27398
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
27425
27399
}
27426
27400
if (!type || !hasBaseType(type, enclosingClass)) {
27427
- return {
27428
- result: TypeChecks.HasDiagnostic,
27429
- message: Diagnostics.Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1_This_is_an_instance_of_class_2,
27430
- args: [symbolToString(prop), typeToString(enclosingClass), typeToString(type)]
27431
- };
27401
+ if (errorNode) {
27402
+ error(errorNode,
27403
+ Diagnostics.Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1_This_is_an_instance_of_class_2,
27404
+ symbolToString(prop), typeToString(enclosingClass), typeToString(type));
27405
+ }
27406
+ return false;
27432
27407
}
27433
- return checkerOk ;
27408
+ return true ;
27434
27409
}
27435
27410
27436
27411
function getThisParameterFromNodeContext(node: Node) {
@@ -28150,8 +28125,22 @@ namespace ts {
28150
28125
}
28151
28126
}
28152
28127
28128
+ /**
28129
+ * Checks if an existing property access is valid for completions purposes.
28130
+ * @param node a property access-like node where we want to check if we can access a property.
28131
+ * This node does not need to be an access of the property we are checking.
28132
+ * e.g. in completions, this node will often be an incomplete property access node, as in `foo.`.
28133
+ * Besides providing a location (i.e. scope) used to check property accessibility, we use this node for
28134
+ * computing whether this is a `super` property access.
28135
+ * @param type the type whose property we are checking
28136
+ * @param property the accessed property's symbol
28137
+ */
28153
28138
function isValidPropertyAccessForCompletions(node: PropertyAccessExpression | ImportTypeNode | QualifiedName, type: Type, property: Symbol): boolean {
28154
- return isValidPropertyAccessWithType(node, node.kind === SyntaxKind.PropertyAccessExpression && node.expression.kind === SyntaxKind.SuperKeyword, property.escapedName, type);
28139
+ return isPropertyAccessible(node,
28140
+ node.kind === SyntaxKind.PropertyAccessExpression && node.expression.kind === SyntaxKind.SuperKeyword,
28141
+ /* isWrite */ false,
28142
+ type,
28143
+ property);
28155
28144
// Previously we validated the 'this' type of methods but this adversely affected performance. See #31377 for more context.
28156
28145
}
28157
28146
@@ -28161,19 +28150,45 @@ namespace ts {
28161
28150
propertyName: __String,
28162
28151
type: Type): boolean {
28163
28152
28153
+ // Short-circuiting for improved performance.
28164
28154
if (type === errorType || isTypeAny(type)) {
28165
28155
return true;
28166
28156
}
28157
+
28167
28158
const prop = getPropertyOfType(type, propertyName);
28168
- if (prop) {
28169
- if (prop.valueDeclaration && isPrivateIdentifierClassElementDeclaration(prop.valueDeclaration)) {
28170
- const declClass = getContainingClass(prop.valueDeclaration);
28171
- return !isOptionalChain(node) && !!findAncestor(node, parent => parent === declClass);
28172
- }
28173
- return checkPropertyAccessibility(node, isSuper, /*writing*/ false, type, prop, /* reportError */ false);
28159
+ return !!prop && isPropertyAccessible(node, isSuper, /* isWrite */ false, type, prop);
28160
+ }
28161
+
28162
+ /**
28163
+ * Checks if a property can be accessed in a location.
28164
+ * The location is given by the `node` parameter.
28165
+ * The node does not need to be a property access.
28166
+ * @param node location where to check property accessibility
28167
+ * @param isSuper whether to consider this a `super` property access, e.g. `super.foo`.
28168
+ * @param isWrite whether this is a write access, e.g. `++foo.x`.
28169
+ * @param type type where the property comes from.
28170
+ * @param property property symbol.
28171
+ */
28172
+ function isPropertyAccessible(
28173
+ node: Node,
28174
+ isSuper: boolean,
28175
+ isWrite: boolean,
28176
+ type: Type,
28177
+ property: Symbol): boolean {
28178
+
28179
+ // Short-circuiting for improved performance.
28180
+ if (type === errorType || isTypeAny(type)) {
28181
+ return true;
28182
+ }
28183
+
28184
+ // A #private property access in an optional chain is an error dealt with by the parser.
28185
+ // The checker does not check for it, so we need to do our own check here.
28186
+ if (property.valueDeclaration && isPrivateIdentifierClassElementDeclaration(property.valueDeclaration)) {
28187
+ const declClass = getContainingClass(property.valueDeclaration);
28188
+ return !isOptionalChain(node) && !!findAncestor(node, parent => parent === declClass);
28174
28189
}
28175
- // In js files properties of unions are allowed in completion
28176
- return isInJSFile (node) && (type.flags & TypeFlags.Union) !== 0 && (type as UnionType).types.some(elementType => isValidPropertyAccessWithType(node , isSuper, propertyName, elementType) );
28190
+
28191
+ return checkPropertyAccessibilityAtLocation (node, isSuper, isWrite, type, property );
28177
28192
}
28178
28193
28179
28194
/**
0 commit comments