Skip to content

Commit 283c50c

Browse files
authored
Merge pull request #12514 from Microsoft/keyofAndForIn
Fix issues related to 'keyof' with 'for-in' and 'in' operator
2 parents 5ab2fec + 12b63d2 commit 283c50c

File tree

7 files changed

+388
-18
lines changed

7 files changed

+388
-18
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)) {

tests/baselines/reference/inOperatorWithInvalidOperands.errors.txt

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(12,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
22
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(13,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
33
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(14,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
4-
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(16,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
5-
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(17,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
64
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(19,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
75
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(20,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
86
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(30,16): error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter
@@ -19,7 +17,7 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv
1917
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(43,17): error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter
2018

2119

22-
==== tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts (19 errors) ====
20+
==== tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts (17 errors) ====
2321
enum E { a }
2422

2523
var x: any;
@@ -42,11 +40,7 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv
4240
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
4341
var ra4 = a4 in x;
4442
var ra5 = null in x;
45-
~~~~
46-
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
4743
var ra6 = undefined in x;
48-
~~~~~~~~~
49-
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
5044
var ra7 = E.a in x;
5145
var ra8 = false in x;
5246
~~~~~
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//// [keyofAndForIn.ts]
2+
3+
// Repro from #12513
4+
5+
function f1<K extends string, T>(obj: { [P in K]: T }, k: K) {
6+
const b = k in obj;
7+
let k1: K;
8+
for (k1 in obj) {
9+
let x1 = obj[k1];
10+
}
11+
for (let k2 in obj) {
12+
let x2 = obj[k2];
13+
}
14+
}
15+
16+
function f2<T>(obj: { [P in keyof T]: T[P] }, k: keyof T) {
17+
const b = k in obj;
18+
let k1: keyof T;
19+
for (k1 in obj) {
20+
let x1 = obj[k1];
21+
}
22+
for (let k2 in obj) {
23+
let x2 = obj[k2];
24+
}
25+
}
26+
27+
function f3<T, K extends keyof T>(obj: { [P in K]: T[P] }, k: K) {
28+
const b = k in obj;
29+
let k1: K;
30+
for (k1 in obj) {
31+
let x1 = obj[k1];
32+
}
33+
for (let k2 in obj) {
34+
let x2 = obj[k2];
35+
}
36+
}
37+
38+
//// [keyofAndForIn.js]
39+
// Repro from #12513
40+
function f1(obj, k) {
41+
var b = k in obj;
42+
var k1;
43+
for (k1 in obj) {
44+
var x1 = obj[k1];
45+
}
46+
for (var k2 in obj) {
47+
var x2 = obj[k2];
48+
}
49+
}
50+
function f2(obj, k) {
51+
var b = k in obj;
52+
var k1;
53+
for (k1 in obj) {
54+
var x1 = obj[k1];
55+
}
56+
for (var k2 in obj) {
57+
var x2 = obj[k2];
58+
}
59+
}
60+
function f3(obj, k) {
61+
var b = k in obj;
62+
var k1;
63+
for (k1 in obj) {
64+
var x1 = obj[k1];
65+
}
66+
for (var k2 in obj) {
67+
var x2 = obj[k2];
68+
}
69+
}
70+
71+
72+
//// [keyofAndForIn.d.ts]
73+
declare function f1<K extends string, T>(obj: {
74+
[P in K]: T;
75+
}, k: K): void;
76+
declare function f2<T>(obj: {
77+
[P in keyof T]: T[P];
78+
}, k: keyof T): void;
79+
declare function f3<T, K extends keyof T>(obj: {
80+
[P in K]: T[P];
81+
}, k: K): void;
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
=== tests/cases/conformance/types/keyof/keyofAndForIn.ts ===
2+
3+
// Repro from #12513
4+
5+
function f1<K extends string, T>(obj: { [P in K]: T }, k: K) {
6+
>f1 : Symbol(f1, Decl(keyofAndForIn.ts, 0, 0))
7+
>K : Symbol(K, Decl(keyofAndForIn.ts, 3, 12))
8+
>T : Symbol(T, Decl(keyofAndForIn.ts, 3, 29))
9+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 3, 33))
10+
>P : Symbol(P, Decl(keyofAndForIn.ts, 3, 41))
11+
>K : Symbol(K, Decl(keyofAndForIn.ts, 3, 12))
12+
>T : Symbol(T, Decl(keyofAndForIn.ts, 3, 29))
13+
>k : Symbol(k, Decl(keyofAndForIn.ts, 3, 54))
14+
>K : Symbol(K, Decl(keyofAndForIn.ts, 3, 12))
15+
16+
const b = k in obj;
17+
>b : Symbol(b, Decl(keyofAndForIn.ts, 4, 9))
18+
>k : Symbol(k, Decl(keyofAndForIn.ts, 3, 54))
19+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 3, 33))
20+
21+
let k1: K;
22+
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 5, 7))
23+
>K : Symbol(K, Decl(keyofAndForIn.ts, 3, 12))
24+
25+
for (k1 in obj) {
26+
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 5, 7))
27+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 3, 33))
28+
29+
let x1 = obj[k1];
30+
>x1 : Symbol(x1, Decl(keyofAndForIn.ts, 7, 11))
31+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 3, 33))
32+
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 5, 7))
33+
}
34+
for (let k2 in obj) {
35+
>k2 : Symbol(k2, Decl(keyofAndForIn.ts, 9, 12))
36+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 3, 33))
37+
38+
let x2 = obj[k2];
39+
>x2 : Symbol(x2, Decl(keyofAndForIn.ts, 10, 11))
40+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 3, 33))
41+
>k2 : Symbol(k2, Decl(keyofAndForIn.ts, 9, 12))
42+
}
43+
}
44+
45+
function f2<T>(obj: { [P in keyof T]: T[P] }, k: keyof T) {
46+
>f2 : Symbol(f2, Decl(keyofAndForIn.ts, 12, 1))
47+
>T : Symbol(T, Decl(keyofAndForIn.ts, 14, 12))
48+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 14, 15))
49+
>P : Symbol(P, Decl(keyofAndForIn.ts, 14, 23))
50+
>T : Symbol(T, Decl(keyofAndForIn.ts, 14, 12))
51+
>T : Symbol(T, Decl(keyofAndForIn.ts, 14, 12))
52+
>P : Symbol(P, Decl(keyofAndForIn.ts, 14, 23))
53+
>k : Symbol(k, Decl(keyofAndForIn.ts, 14, 45))
54+
>T : Symbol(T, Decl(keyofAndForIn.ts, 14, 12))
55+
56+
const b = k in obj;
57+
>b : Symbol(b, Decl(keyofAndForIn.ts, 15, 9))
58+
>k : Symbol(k, Decl(keyofAndForIn.ts, 14, 45))
59+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 14, 15))
60+
61+
let k1: keyof T;
62+
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 16, 7))
63+
>T : Symbol(T, Decl(keyofAndForIn.ts, 14, 12))
64+
65+
for (k1 in obj) {
66+
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 16, 7))
67+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 14, 15))
68+
69+
let x1 = obj[k1];
70+
>x1 : Symbol(x1, Decl(keyofAndForIn.ts, 18, 11))
71+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 14, 15))
72+
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 16, 7))
73+
}
74+
for (let k2 in obj) {
75+
>k2 : Symbol(k2, Decl(keyofAndForIn.ts, 20, 12))
76+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 14, 15))
77+
78+
let x2 = obj[k2];
79+
>x2 : Symbol(x2, Decl(keyofAndForIn.ts, 21, 11))
80+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 14, 15))
81+
>k2 : Symbol(k2, Decl(keyofAndForIn.ts, 20, 12))
82+
}
83+
}
84+
85+
function f3<T, K extends keyof T>(obj: { [P in K]: T[P] }, k: K) {
86+
>f3 : Symbol(f3, Decl(keyofAndForIn.ts, 23, 1))
87+
>T : Symbol(T, Decl(keyofAndForIn.ts, 25, 12))
88+
>K : Symbol(K, Decl(keyofAndForIn.ts, 25, 14))
89+
>T : Symbol(T, Decl(keyofAndForIn.ts, 25, 12))
90+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 25, 34))
91+
>P : Symbol(P, Decl(keyofAndForIn.ts, 25, 42))
92+
>K : Symbol(K, Decl(keyofAndForIn.ts, 25, 14))
93+
>T : Symbol(T, Decl(keyofAndForIn.ts, 25, 12))
94+
>P : Symbol(P, Decl(keyofAndForIn.ts, 25, 42))
95+
>k : Symbol(k, Decl(keyofAndForIn.ts, 25, 58))
96+
>K : Symbol(K, Decl(keyofAndForIn.ts, 25, 14))
97+
98+
const b = k in obj;
99+
>b : Symbol(b, Decl(keyofAndForIn.ts, 26, 9))
100+
>k : Symbol(k, Decl(keyofAndForIn.ts, 25, 58))
101+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 25, 34))
102+
103+
let k1: K;
104+
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 27, 7))
105+
>K : Symbol(K, Decl(keyofAndForIn.ts, 25, 14))
106+
107+
for (k1 in obj) {
108+
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 27, 7))
109+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 25, 34))
110+
111+
let x1 = obj[k1];
112+
>x1 : Symbol(x1, Decl(keyofAndForIn.ts, 29, 11))
113+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 25, 34))
114+
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 27, 7))
115+
}
116+
for (let k2 in obj) {
117+
>k2 : Symbol(k2, Decl(keyofAndForIn.ts, 31, 12))
118+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 25, 34))
119+
120+
let x2 = obj[k2];
121+
>x2 : Symbol(x2, Decl(keyofAndForIn.ts, 32, 11))
122+
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 25, 34))
123+
>k2 : Symbol(k2, Decl(keyofAndForIn.ts, 31, 12))
124+
}
125+
}

0 commit comments

Comments
 (0)