From 3344a42686b4a3b4470497ef20a3f3e3aea11cd5 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 7 Jan 2016 10:34:22 -0800 Subject: [PATCH] Check for numeric index signature instead of array-like type Better error message when object with numeric index signature is indexed with a string --- src/compiler/checker.ts | 21 +++++++++++++++------ src/compiler/diagnosticMessages.json | 4 ++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 62ab2080d28..ca562a5c353 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8581,10 +8581,17 @@ namespace ts { } /** - * Return true if given node is an expression consisting of an identifier (possibly parenthesized) - * that references a variable declared in a containing for-in statement for an array-like object. + * Return true if the given type is considered to have numeric property names. */ - function isForInVariableForArrayLikeObject(expr: Expression) { + function hasNumericPropertyNames(type: Type) { + return getIndexTypeOfType(type, IndexKind.Number) && !getIndexTypeOfType(type, IndexKind.String); + } + + /** + * Return true if given node is an expression consisting of an identifier (possibly parenthesized) + * that references a for-in variable for an object with numeric property names. + */ + function isForInVariableForNumericPropertyNames(expr: Expression) { const e = skipParenthesizedNodes(expr); if (e.kind === SyntaxKind.Identifier) { const symbol = getResolvedSymbol(e); @@ -8595,7 +8602,7 @@ namespace ts { if (node.kind === SyntaxKind.ForInStatement && child === (node).statement && getForInVariableSymbol(node) === symbol && - isArrayLikeType(checkExpression((node).expression))) { + hasNumericPropertyNames(checkExpression((node).expression))) { return true; } child = node; @@ -8666,7 +8673,7 @@ namespace ts { if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) { // Try to use a number indexer. - if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike) || isForInVariableForArrayLikeObject(node.argumentExpression)) { + if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike) || isForInVariableForNumericPropertyNames(node.argumentExpression)) { const numberIndexType = getIndexTypeOfType(objectType, IndexKind.Number); if (numberIndexType) { return numberIndexType; @@ -8681,7 +8688,9 @@ namespace ts { // Fall back to any. if (compilerOptions.noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors && !isTypeAny(objectType)) { - error(node, Diagnostics.Index_signature_of_object_type_implicitly_has_an_any_type); + error(node, getIndexTypeOfType(objectType, IndexKind.Number) ? + Diagnostics.Element_implicitly_has_an_any_type_because_index_expression_is_not_of_type_number : + Diagnostics.Index_signature_of_object_type_implicitly_has_an_any_type); } return anyType; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index f7268d6d990..4a0a1dba5ac 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2422,6 +2422,10 @@ "category": "Error", "code": 7013 }, + "Element implicitly has an 'any' type because index expression is not of type 'number'.": { + "category": "Error", + "code": 7015 + }, "Property '{0}' implicitly has type 'any', because its 'set' accessor lacks a type annotation.": { "category": "Error", "code": 7016