diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 7b4fd5803e3..b105721ba13 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -5777,7 +5777,7 @@ namespace ts {
getIndexInfoOfType(objectType, IndexKind.String) ||
undefined;
if (indexInfo) {
- if (accessExpression && isAssignmentTarget(accessExpression) && indexInfo.isReadonly) {
+ if (accessExpression && indexInfo.isReadonly && (isAssignmentTarget(accessExpression) || isDeleteTarget(accessExpression))) {
error(accessExpression, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType));
return unknownType;
}
@@ -13550,9 +13550,16 @@ namespace ts {
function checkDeleteExpression(node: DeleteExpression): Type {
checkExpression(node.expression);
- checkReferenceExpression(node.expression,
- Diagnostics.The_operand_of_a_delete_operator_must_be_a_property_reference,
- Diagnostics.The_operand_of_a_delete_operator_cannot_be_a_read_only_property);
+ const expr = skipParentheses(node.expression);
+ if (expr.kind !== SyntaxKind.PropertyAccessExpression && expr.kind !== SyntaxKind.ElementAccessExpression) {
+ error(expr, Diagnostics.The_operand_of_a_delete_operator_must_be_a_property_reference);
+ return booleanType;
+ }
+ const links = getNodeLinks(expr);
+ const symbol = getExportSymbolOfValueSymbolIfExported(links.resolvedSymbol);
+ if (symbol && isReadonlySymbol(symbol)) {
+ error(expr, Diagnostics.The_operand_of_a_delete_operator_cannot_be_a_read_only_property);
+ }
return booleanType;
}
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index e4c92c502a4..9e1b35c60da 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2651,7 +2651,6 @@ namespace ts {
resolvedType?: Type; // Cached type of type node
resolvedSignature?: Signature; // Cached signature of signature node or call expression
resolvedSymbol?: Symbol; // Cached name resolution result
- resolvedIndexInfo?: IndexInfo; // Cached indexing info resolution result
enumMemberValue?: number; // Constant value of enum member
isVisible?: boolean; // Is this node visible
hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 299f91be662..8e7b11ccf91 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -1,4 +1,4 @@
-///
+///
/* @internal */
namespace ts {
@@ -1666,6 +1666,18 @@ namespace ts {
return getAssignmentTargetKind(node) !== AssignmentKind.None;
}
+ // a node is delete target iff. it is PropertyAccessExpression/ElementAccessExpression with parentheses skipped
+ export function isDeleteTarget(node: Node): boolean {
+ if (node.kind !== SyntaxKind.PropertyAccessExpression && node.kind !== SyntaxKind.ElementAccessExpression) {
+ return false;
+ }
+ node = node.parent;
+ while (node && node.kind === SyntaxKind.ParenthesizedExpression) {
+ node = node.parent;
+ }
+ return node && node.kind === SyntaxKind.DeleteExpression;
+ }
+
export function isNodeDescendantOf(node: Node, ancestor: Node): boolean {
while (node) {
if (node === ancestor) return true;