mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-16 07:13:45 -05:00
Error on accessing private property through destructuring assignment, and mark property used (#26381)
* Error on accessing private property through destructuring assignment, and mark property used * Factor out getTypeOfObjectLiteralDestructuringProperty
This commit is contained in:
@@ -17736,17 +17736,15 @@ namespace ts {
|
||||
* Check whether the requested property access is valid.
|
||||
* Returns true if node is a valid property access, and false otherwise.
|
||||
* @param node The node to be checked.
|
||||
* @param left The left hand side of the property access (e.g.: the super in `super.foo`).
|
||||
* @param type The type of left.
|
||||
* @param prop The symbol for the right hand side of the property access.
|
||||
* @param isSuper True if the access is from `super.`.
|
||||
* @param type The type of the object whose property is being accessed. (Not the type of the property.)
|
||||
* @param prop The symbol for the property being accessed.
|
||||
*/
|
||||
function checkPropertyAccessibility(node: PropertyAccessExpression | QualifiedName | VariableLikeDeclaration | ImportTypeNode, left: Expression | QualifiedName | ImportTypeNode, type: Type, prop: Symbol): boolean {
|
||||
function checkPropertyAccessibility(
|
||||
node: PropertyAccessExpression | QualifiedName | PropertyAccessExpression | VariableDeclaration | ParameterDeclaration | ImportTypeNode | PropertyAssignment | ShorthandPropertyAssignment | BindingElement,
|
||||
isSuper: boolean, type: Type, prop: Symbol): boolean {
|
||||
const flags = getDeclarationModifierFlagsFromSymbol(prop);
|
||||
const errorNode = node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.VariableDeclaration ?
|
||||
node.name :
|
||||
node.kind === SyntaxKind.ImportType ?
|
||||
node :
|
||||
(<QualifiedName>node).right;
|
||||
const errorNode = node.kind === SyntaxKind.QualifiedName ? node.right : node.kind === SyntaxKind.ImportType ? node : node.name;
|
||||
|
||||
if (getCheckFlags(prop) & CheckFlags.ContainsPrivate) {
|
||||
// Synthetic property with private constituent property
|
||||
@@ -17754,7 +17752,7 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (left.kind === SyntaxKind.SuperKeyword) {
|
||||
if (isSuper) {
|
||||
// TS 1.0 spec (April 2014): 4.8.2
|
||||
// - In a constructor, instance member function, instance member accessor, or
|
||||
// instance member variable initializer where this references a derived class instance,
|
||||
@@ -17807,7 +17805,7 @@ namespace ts {
|
||||
// Property is known to be protected at this point
|
||||
|
||||
// All protected properties of a supertype are accessible in a super access
|
||||
if (left.kind === SyntaxKind.SuperKeyword) {
|
||||
if (isSuper) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -17940,7 +17938,7 @@ namespace ts {
|
||||
checkPropertyNotUsedBeforeDeclaration(prop, node, right);
|
||||
markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword);
|
||||
getNodeLinks(node).resolvedSymbol = prop;
|
||||
checkPropertyAccessibility(node, left, apparentType, prop);
|
||||
checkPropertyAccessibility(node, left.kind === SyntaxKind.SuperKeyword, apparentType, prop);
|
||||
if (assignmentKind) {
|
||||
if (isReferenceToReadonlyEntity(<Expression>node, prop) || isReferenceThroughNamespaceImport(<Expression>node)) {
|
||||
error(right, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, idText(right));
|
||||
@@ -18174,16 +18172,16 @@ namespace ts {
|
||||
function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName | ImportTypeNode, propertyName: __String): boolean {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
return isValidPropertyAccessWithType(node, node.expression, propertyName, getWidenedType(checkExpression(node.expression)));
|
||||
return isValidPropertyAccessWithType(node, node.expression.kind === SyntaxKind.SuperKeyword, propertyName, getWidenedType(checkExpression(node.expression)));
|
||||
case SyntaxKind.QualifiedName:
|
||||
return isValidPropertyAccessWithType(node, node.left, propertyName, getWidenedType(checkExpression(node.left)));
|
||||
return isValidPropertyAccessWithType(node, /*isSuper*/ false, propertyName, getWidenedType(checkExpression(node.left)));
|
||||
case SyntaxKind.ImportType:
|
||||
return isValidPropertyAccessWithType(node, node, propertyName, getTypeFromTypeNode(node));
|
||||
return isValidPropertyAccessWithType(node, /*isSuper*/ false, propertyName, getTypeFromTypeNode(node));
|
||||
}
|
||||
}
|
||||
|
||||
function isValidPropertyAccessForCompletions(node: PropertyAccessExpression | ImportTypeNode, type: Type, property: Symbol): boolean {
|
||||
return isValidPropertyAccessWithType(node, node.kind === SyntaxKind.ImportType ? node : node.expression, property.escapedName, type)
|
||||
return isValidPropertyAccessWithType(node, node.kind !== SyntaxKind.ImportType && node.expression.kind === SyntaxKind.SuperKeyword, property.escapedName, type)
|
||||
&& (!(property.flags & SymbolFlags.Method) || isValidMethodAccess(property, type));
|
||||
}
|
||||
function isValidMethodAccess(method: Symbol, actualThisType: Type): boolean {
|
||||
@@ -18206,7 +18204,7 @@ namespace ts {
|
||||
|
||||
function isValidPropertyAccessWithType(
|
||||
node: PropertyAccessExpression | QualifiedName | ImportTypeNode,
|
||||
left: LeftHandSideExpression | QualifiedName | ImportTypeNode,
|
||||
isSuper: boolean,
|
||||
propertyName: __String,
|
||||
type: Type): boolean {
|
||||
|
||||
@@ -18214,9 +18212,9 @@ namespace ts {
|
||||
return true;
|
||||
}
|
||||
const prop = getPropertyOfType(type, propertyName);
|
||||
return prop ? checkPropertyAccessibility(node, left, type, prop)
|
||||
return prop ? checkPropertyAccessibility(node, isSuper, type, prop)
|
||||
// In js files properties of unions are allowed in completion
|
||||
: isInJavaScriptFile(node) && (type.flags & TypeFlags.Union) !== 0 && (<UnionType>type).types.some(elementType => isValidPropertyAccessWithType(node, left, propertyName, elementType));
|
||||
: isInJavaScriptFile(node) && (type.flags & TypeFlags.Union) !== 0 && (<UnionType>type).types.some(elementType => isValidPropertyAccessWithType(node, isSuper, propertyName, elementType));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -21093,19 +21091,19 @@ namespace ts {
|
||||
return booleanType;
|
||||
}
|
||||
|
||||
function checkObjectLiteralAssignment(node: ObjectLiteralExpression, sourceType: Type): Type {
|
||||
function checkObjectLiteralAssignment(node: ObjectLiteralExpression, sourceType: Type, rightIsThis?: boolean): Type {
|
||||
const properties = node.properties;
|
||||
if (strictNullChecks && properties.length === 0) {
|
||||
return checkNonNullType(sourceType, node);
|
||||
}
|
||||
for (const p of properties) {
|
||||
checkObjectLiteralDestructuringPropertyAssignment(sourceType, p, properties);
|
||||
checkObjectLiteralDestructuringPropertyAssignment(sourceType, p, properties, rightIsThis);
|
||||
}
|
||||
return sourceType;
|
||||
}
|
||||
|
||||
/** Note: If property cannot be a SpreadAssignment, then allProperties does not need to be provided */
|
||||
function checkObjectLiteralDestructuringPropertyAssignment(objectLiteralType: Type, property: ObjectLiteralElementLike, allProperties?: NodeArray<ObjectLiteralElementLike>) {
|
||||
function checkObjectLiteralDestructuringPropertyAssignment(objectLiteralType: Type, property: ObjectLiteralElementLike, allProperties?: NodeArray<ObjectLiteralElementLike>, rightIsThis = false) {
|
||||
if (property.kind === SyntaxKind.PropertyAssignment || property.kind === SyntaxKind.ShorthandPropertyAssignment) {
|
||||
const name = property.name;
|
||||
if (name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
@@ -21115,20 +21113,10 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const text = getTextOfPropertyName(name);
|
||||
const type = isTypeAny(objectLiteralType)
|
||||
? objectLiteralType
|
||||
: getTypeOfPropertyOfType(objectLiteralType, text) ||
|
||||
isNumericLiteralName(text) && getIndexTypeOfType(objectLiteralType, IndexKind.Number) ||
|
||||
getIndexTypeOfType(objectLiteralType, IndexKind.String);
|
||||
const type = getTypeOfObjectLiteralDestructuringProperty(objectLiteralType, name, property, rightIsThis);
|
||||
if (type) {
|
||||
if (property.kind === SyntaxKind.ShorthandPropertyAssignment) {
|
||||
return checkDestructuringAssignment(property, type);
|
||||
}
|
||||
else {
|
||||
// non-shorthand property assignments should always have initializers
|
||||
return checkDestructuringAssignment(property.initializer, type);
|
||||
}
|
||||
// non-shorthand property assignments should always have initializers
|
||||
return checkDestructuringAssignment(property.kind === SyntaxKind.ShorthandPropertyAssignment ? property : property.initializer, type);
|
||||
}
|
||||
else {
|
||||
error(name, Diagnostics.Type_0_has_no_property_1_and_no_string_index_signature, typeToString(objectLiteralType), declarationNameToString(name));
|
||||
@@ -21153,6 +21141,25 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeOfObjectLiteralDestructuringProperty(objectLiteralType: Type, name: PropertyName, property: PropertyAssignment | ShorthandPropertyAssignment, rightIsThis: boolean) {
|
||||
if (isTypeAny(objectLiteralType)) {
|
||||
return objectLiteralType;
|
||||
}
|
||||
|
||||
let type: Type | undefined;
|
||||
const text = getTextOfPropertyName(name);
|
||||
if (text) { // TODO: GH#26379
|
||||
const prop = getPropertyOfType(objectLiteralType, text);
|
||||
if (prop) {
|
||||
markPropertyAsReferenced(prop, property, rightIsThis);
|
||||
checkPropertyAccessibility(property, /*isSuper*/ false, objectLiteralType, prop);
|
||||
type = getTypeOfSymbol(prop);
|
||||
}
|
||||
type = type || (isNumericLiteralName(text) ? getIndexTypeOfType(objectLiteralType, IndexKind.Number) : undefined);
|
||||
}
|
||||
return type || getIndexTypeOfType(objectLiteralType, IndexKind.String);
|
||||
}
|
||||
|
||||
function checkArrayLiteralAssignment(node: ArrayLiteralExpression, sourceType: Type, checkMode?: CheckMode): Type {
|
||||
const elements = node.elements;
|
||||
if (languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) {
|
||||
@@ -21214,7 +21221,7 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function checkDestructuringAssignment(exprOrAssignment: Expression | ShorthandPropertyAssignment, sourceType: Type, checkMode?: CheckMode): Type {
|
||||
function checkDestructuringAssignment(exprOrAssignment: Expression | ShorthandPropertyAssignment, sourceType: Type, checkMode?: CheckMode, rightIsThis?: boolean): Type {
|
||||
let target: Expression;
|
||||
if (exprOrAssignment.kind === SyntaxKind.ShorthandPropertyAssignment) {
|
||||
const prop = <ShorthandPropertyAssignment>exprOrAssignment;
|
||||
@@ -21238,7 +21245,7 @@ namespace ts {
|
||||
target = (<BinaryExpression>target).left;
|
||||
}
|
||||
if (target.kind === SyntaxKind.ObjectLiteralExpression) {
|
||||
return checkObjectLiteralAssignment(<ObjectLiteralExpression>target, sourceType);
|
||||
return checkObjectLiteralAssignment(<ObjectLiteralExpression>target, sourceType, rightIsThis);
|
||||
}
|
||||
if (target.kind === SyntaxKind.ArrayLiteralExpression) {
|
||||
return checkArrayLiteralAssignment(<ArrayLiteralExpression>target, sourceType, checkMode);
|
||||
@@ -21337,7 +21344,7 @@ namespace ts {
|
||||
function checkBinaryLikeExpression(left: Expression, operatorToken: Node, right: Expression, checkMode?: CheckMode, errorNode?: Node): Type {
|
||||
const operator = operatorToken.kind;
|
||||
if (operator === SyntaxKind.EqualsToken && (left.kind === SyntaxKind.ObjectLiteralExpression || left.kind === SyntaxKind.ArrayLiteralExpression)) {
|
||||
return checkDestructuringAssignment(left, checkExpression(right, checkMode), checkMode);
|
||||
return checkDestructuringAssignment(left, checkExpression(right, checkMode), checkMode, right.kind === SyntaxKind.ThisKeyword);
|
||||
}
|
||||
let leftType: Type;
|
||||
if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken) {
|
||||
@@ -24400,7 +24407,7 @@ namespace ts {
|
||||
const property = getPropertyOfType(parentType!, nameText)!; // TODO: GH#18217
|
||||
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined, /*isThisAccess*/ false); // A destructuring is never a write-only reference.
|
||||
if (parent.initializer && property && !isComputedPropertyName(name)) {
|
||||
checkPropertyAccessibility(parent, parent.initializer, parentType!, property);
|
||||
checkPropertyAccessibility(parent, parent.initializer.kind === SyntaxKind.SuperKeyword, parentType!, property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user