Skip to content

Commit 46ca0ba

Browse files
committed
Fix multiple 'keyof' issues with 'for-in' and 'in' operator
1 parent 5ba678a commit 46ca0ba

File tree

1 file changed

+10
-7
lines changed

1 file changed

+10
-7
lines changed

src/compiler/checker.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3205,7 +3205,7 @@ namespace ts {
32053205
// right hand expression is of a type parameter type.
32063206
if (declaration.parent.parent.kind === SyntaxKind.ForInStatement) {
32073207
const indexType = getIndexType(checkNonNullExpression((<ForInStatement>declaration.parent.parent).expression));
3208-
return indexType.flags & TypeFlags.Index ? indexType : stringType;
3208+
return indexType.flags & (TypeFlags.TypeParameter | TypeFlags.Index) ? indexType : stringType;
32093209
}
32103210

32113211
if (declaration.parent.parent.kind === SyntaxKind.ForOfStatement) {
@@ -5920,6 +5920,11 @@ namespace ts {
59205920
getLiteralTypeFromPropertyNames(type);
59215921
}
59225922

5923+
function getIndexTypeOrString(type: Type): Type {
5924+
const indexType = getIndexType(type);
5925+
return indexType !== neverType ? indexType : stringType;
5926+
}
5927+
59235928
function getTypeFromTypeOperatorNode(node: TypeOperatorNode) {
59245929
const links = getNodeLinks(node);
59255930
if (!links.resolvedType) {
@@ -6018,8 +6023,7 @@ namespace ts {
60186023
// meaningfully access the properties of the object type. In those cases, we first check that the
60196024
// index type is assignable to 'keyof T' for the object type.
60206025
if (accessNode) {
6021-
const keyType = indexType.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(<TypeParameter>indexType) || emptyObjectType : indexType;
6022-
if (!isTypeAssignableTo(keyType, getIndexType(objectType))) {
6026+
if (!isTypeAssignableTo(indexType, getIndexType(objectType))) {
60236027
error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType));
60246028
return unknownType;
60256029
}
@@ -14275,7 +14279,7 @@ namespace ts {
1427514279
// The in operator requires the left operand to be of type Any, the String primitive type, or the Number primitive type,
1427614280
// and the right operand to be of type Any, an object type, or a type parameter type.
1427714281
// The result is always of the Boolean primitive type.
14278-
if (!isTypeAnyOrAllConstituentTypesHaveKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) {
14282+
if (!(isTypeComparableTo(leftType, stringType) || isTypeOfKind(leftType, TypeFlags.NumberLike | TypeFlags.ESSymbol))) {
1427914283
error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol);
1428014284
}
1428114285
if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeParameter | TypeFlags.IndexedAccess)) {
@@ -17166,6 +17170,7 @@ namespace ts {
1716617170
// Grammar checking
1716717171
checkGrammarForInOrForOfStatement(node);
1716817172

17173+
const rightType = checkNonNullExpression(node.expression);
1716917174
// TypeScript 1.0 spec (April 2014): 5.4
1717017175
// In a 'for-in' statement of the form
1717117176
// for (let VarDecl in Expr) Statement
@@ -17176,7 +17181,6 @@ namespace ts {
1717617181
if (variable && isBindingPattern(variable.name)) {
1717717182
error(variable.name, Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern);
1717817183
}
17179-
1718017184
checkForInOrForOfVariableDeclaration(node);
1718117185
}
1718217186
else {
@@ -17189,7 +17193,7 @@ namespace ts {
1718917193
if (varExpr.kind === SyntaxKind.ArrayLiteralExpression || varExpr.kind === SyntaxKind.ObjectLiteralExpression) {
1719017194
error(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern);
1719117195
}
17192-
else if (!isTypeAnyOrAllConstituentTypesHaveKind(leftType, TypeFlags.StringLike)) {
17196+
else if (!isTypeAssignableTo(getIndexTypeOrString(rightType), leftType)) {
1719317197
error(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_of_type_string_or_any);
1719417198
}
1719517199
else {
@@ -17198,7 +17202,6 @@ namespace ts {
1719817202
}
1719917203
}
1720017204

17201-
const rightType = checkNonNullExpression(node.expression);
1720217205
// unknownType is returned i.e. if node.expression is identifier whose name cannot be resolved
1720317206
// in this case error about missing name is already reported - do not report extra one
1720417207
if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeParameter | TypeFlags.IndexedAccess)) {

0 commit comments

Comments
 (0)