Skip to content

Commit a05b1de

Browse files
committed
Change for-in iteration variable type from any to string
1 parent ba0f7f5 commit a05b1de

File tree

1 file changed

+24
-5
lines changed

1 file changed

+24
-5
lines changed

src/compiler/checker.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2520,9 +2520,9 @@ namespace ts {
25202520

25212521
// Return the inferred type for a variable, parameter, or property declaration
25222522
function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration): Type {
2523-
// A variable declared in a for..in statement is always of type any
2523+
// A variable declared in a for..in statement is always of type string
25242524
if (declaration.parent.parent.kind === SyntaxKind.ForInStatement) {
2525-
return anyType;
2525+
return stringType;
25262526
}
25272527

25282528
if (declaration.parent.parent.kind === SyntaxKind.ForOfStatement) {
@@ -8563,6 +8563,23 @@ namespace ts {
85638563
return true;
85648564
}
85658565

8566+
/**
8567+
* Return true if given node is an expression consisting of an identifier (possibly parenthesized)
8568+
* that references a variable declared in a for-in statement for an array-like object.
8569+
*/
8570+
function isForInVariableForArrayLikeObject(node: Expression) {
8571+
const e = skipParenthesizedNodes(node);
8572+
if (e.kind === SyntaxKind.Identifier) {
8573+
const symbol = getResolvedSymbol(<Identifier>e);
8574+
if (symbol.flags & SymbolFlags.Variable) {
8575+
const parent = symbol.valueDeclaration.parent.parent;
8576+
return parent.kind === SyntaxKind.ForInStatement &&
8577+
isArrayLikeType(checkExpression((<ForInStatement>parent).expression));
8578+
}
8579+
}
8580+
return false;
8581+
}
8582+
85668583
function checkIndexedAccess(node: ElementAccessExpression): Type {
85678584
// Grammar checking
85688585
if (!node.argumentExpression) {
@@ -8623,7 +8640,7 @@ namespace ts {
86238640
if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) {
86248641

86258642
// Try to use a number indexer.
8626-
if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike)) {
8643+
if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike) || isForInVariableForArrayLikeObject(node.argumentExpression)) {
86278644
const numberIndexType = getIndexTypeOfType(objectType, IndexKind.Number);
86288645
if (numberIndexType) {
86298646
return numberIndexType;
@@ -12697,7 +12714,8 @@ namespace ts {
1269712714
}
1269812715
// For a binding pattern, validate the initializer and exit
1269912716
if (isBindingPattern(node.name)) {
12700-
if (node.initializer) {
12717+
// Don't validate for-in initializer as it is already an error
12718+
if (node.initializer && node.parent.parent.kind !== SyntaxKind.ForInStatement) {
1270112719
checkTypeAssignableTo(checkExpressionCached(node.initializer), getWidenedTypeForVariableLikeDeclaration(node), node, /*headMessage*/ undefined);
1270212720
checkParameterInitializer(node);
1270312721
}
@@ -12707,7 +12725,8 @@ namespace ts {
1270712725
const type = getTypeOfVariableOrParameterOrProperty(symbol);
1270812726
if (node === symbol.valueDeclaration) {
1270912727
// Node is the primary declaration of the symbol, just validate the initializer
12710-
if (node.initializer) {
12728+
// Don't validate for-in initializer as it is already an error
12729+
if (node.initializer && node.parent.parent.kind !== SyntaxKind.ForInStatement) {
1271112730
checkTypeAssignableTo(checkExpressionCached(node.initializer), type, node, /*headMessage*/ undefined);
1271212731
checkParameterInitializer(node);
1271312732
}

0 commit comments

Comments
 (0)