Skip to content

Commit

Permalink
Change for-in iteration variable type from any to string
Browse files Browse the repository at this point in the history
  • Loading branch information
ahejlsberg committed Jan 6, 2016
1 parent ba0f7f5 commit a05b1de
Showing 1 changed file with 24 additions and 5 deletions.
29 changes: 24 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2520,9 +2520,9 @@ namespace ts {

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

if (declaration.parent.parent.kind === SyntaxKind.ForOfStatement) {
Expand Down Expand Up @@ -8563,6 +8563,23 @@ namespace ts {
return true;
}

/**
* Return true if given node is an expression consisting of an identifier (possibly parenthesized)
* that references a variable declared in a for-in statement for an array-like object.
*/
function isForInVariableForArrayLikeObject(node: Expression) {
const e = skipParenthesizedNodes(node);
if (e.kind === SyntaxKind.Identifier) {
const symbol = getResolvedSymbol(<Identifier>e);
if (symbol.flags & SymbolFlags.Variable) {
const parent = symbol.valueDeclaration.parent.parent;
return parent.kind === SyntaxKind.ForInStatement &&
isArrayLikeType(checkExpression((<ForInStatement>parent).expression));
}
}
return false;
}

function checkIndexedAccess(node: ElementAccessExpression): Type {
// Grammar checking
if (!node.argumentExpression) {
Expand Down Expand Up @@ -8623,7 +8640,7 @@ namespace ts {
if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) {

// Try to use a number indexer.
if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike)) {
if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike) || isForInVariableForArrayLikeObject(node.argumentExpression)) {

This comment has been minimized.

Copy link
@sandersn

sandersn Jan 6, 2016

Member

Can isForInVariableForArrayLikeObject ever be true when indexType is an ESSymbol? If so, can you give an example in the new tests because I don't know what it would look like. If not, maybe update the comment.

This comment has been minimized.

Copy link
@ahejlsberg

ahejlsberg Jan 7, 2016

Author Member

A for-in variable can only be of type any or string, so ESSymbol doesn't come into play here.

const numberIndexType = getIndexTypeOfType(objectType, IndexKind.Number);
if (numberIndexType) {
return numberIndexType;
Expand Down Expand Up @@ -12697,7 +12714,8 @@ namespace ts {
}
// For a binding pattern, validate the initializer and exit
if (isBindingPattern(node.name)) {
if (node.initializer) {
// Don't validate for-in initializer as it is already an error
if (node.initializer && node.parent.parent.kind !== SyntaxKind.ForInStatement) {
checkTypeAssignableTo(checkExpressionCached(node.initializer), getWidenedTypeForVariableLikeDeclaration(node), node, /*headMessage*/ undefined);
checkParameterInitializer(node);
}
Expand All @@ -12707,7 +12725,8 @@ namespace ts {
const type = getTypeOfVariableOrParameterOrProperty(symbol);
if (node === symbol.valueDeclaration) {
// Node is the primary declaration of the symbol, just validate the initializer
if (node.initializer) {
// Don't validate for-in initializer as it is already an error
if (node.initializer && node.parent.parent.kind !== SyntaxKind.ForInStatement) {
checkTypeAssignableTo(checkExpressionCached(node.initializer), type, node, /*headMessage*/ undefined);
checkParameterInitializer(node);
}
Expand Down

1 comment on commit a05b1de

@sandersn
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Please sign in to comment.