Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3205,7 +3205,7 @@ namespace ts {
// right hand expression is of a type parameter type.
if (declaration.parent.parent.kind === SyntaxKind.ForInStatement) {
const indexType = getIndexType(checkNonNullExpression((<ForInStatement>declaration.parent.parent).expression));
return indexType.flags & TypeFlags.Index ? indexType : stringType;
return indexType.flags & (TypeFlags.TypeParameter | TypeFlags.Index) ? indexType : stringType;
}

if (declaration.parent.parent.kind === SyntaxKind.ForOfStatement) {
Expand Down Expand Up @@ -5920,6 +5920,11 @@ namespace ts {
getLiteralTypeFromPropertyNames(type);
}

function getIndexTypeOrString(type: Type): Type {
const indexType = getIndexType(type);
return indexType !== neverType ? indexType : stringType;
}

function getTypeFromTypeOperatorNode(node: TypeOperatorNode) {
const links = getNodeLinks(node);
if (!links.resolvedType) {
Expand Down Expand Up @@ -6018,8 +6023,7 @@ namespace ts {
// meaningfully access the properties of the object type. In those cases, we first check that the
// index type is assignable to 'keyof T' for the object type.
if (accessNode) {
const keyType = indexType.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(<TypeParameter>indexType) || emptyObjectType : indexType;
if (!isTypeAssignableTo(keyType, getIndexType(objectType))) {
if (!isTypeAssignableTo(indexType, getIndexType(objectType))) {
error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType));
return unknownType;
}
Expand Down Expand Up @@ -14275,7 +14279,7 @@ namespace ts {
// The in operator requires the left operand to be of type Any, the String primitive type, or the Number primitive type,
// and the right operand to be of type Any, an object type, or a type parameter type.
// The result is always of the Boolean primitive type.
if (!isTypeAnyOrAllConstituentTypesHaveKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) {
if (!(isTypeComparableTo(leftType, stringType) || isTypeOfKind(leftType, TypeFlags.NumberLike | TypeFlags.ESSymbol))) {
error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol);
}
if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeParameter | TypeFlags.IndexedAccess)) {
Expand Down Expand Up @@ -17166,6 +17170,7 @@ namespace ts {
// Grammar checking
checkGrammarForInOrForOfStatement(node);

const rightType = checkNonNullExpression(node.expression);
// TypeScript 1.0 spec (April 2014): 5.4
// In a 'for-in' statement of the form
// for (let VarDecl in Expr) Statement
Expand All @@ -17176,7 +17181,6 @@ namespace ts {
if (variable && isBindingPattern(variable.name)) {
error(variable.name, Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern);
}

checkForInOrForOfVariableDeclaration(node);
}
else {
Expand All @@ -17189,7 +17193,7 @@ namespace ts {
if (varExpr.kind === SyntaxKind.ArrayLiteralExpression || varExpr.kind === SyntaxKind.ObjectLiteralExpression) {
error(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern);
}
else if (!isTypeAnyOrAllConstituentTypesHaveKind(leftType, TypeFlags.StringLike)) {
else if (!isTypeAssignableTo(getIndexTypeOrString(rightType), leftType)) {
error(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_of_type_string_or_any);
}
else {
Expand All @@ -17198,7 +17202,6 @@ namespace ts {
}
}

const rightType = checkNonNullExpression(node.expression);
// unknownType is returned i.e. if node.expression is identifier whose name cannot be resolved
// in this case error about missing name is already reported - do not report extra one
if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeParameter | TypeFlags.IndexedAccess)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
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'.
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'.
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'.
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'.
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'.
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'.
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'.
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
Expand All @@ -19,7 +17,7 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv
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


==== tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts (19 errors) ====
==== tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts (17 errors) ====
enum E { a }

var x: any;
Expand All @@ -42,11 +40,7 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
var ra4 = a4 in x;
var ra5 = null in x;
~~~~
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
var ra6 = undefined in x;
~~~~~~~~~
!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.
var ra7 = E.a in x;
var ra8 = false in x;
~~~~~
Expand Down
81 changes: 81 additions & 0 deletions tests/baselines/reference/keyofAndForIn.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//// [keyofAndForIn.ts]

// Repro from #12513

function f1<K extends string, T>(obj: { [P in K]: T }, k: K) {
const b = k in obj;
let k1: K;
for (k1 in obj) {
let x1 = obj[k1];
}
for (let k2 in obj) {
let x2 = obj[k2];
}
}

function f2<T>(obj: { [P in keyof T]: T[P] }, k: keyof T) {
const b = k in obj;
let k1: keyof T;
for (k1 in obj) {
let x1 = obj[k1];
}
for (let k2 in obj) {
let x2 = obj[k2];
}
}

function f3<T, K extends keyof T>(obj: { [P in K]: T[P] }, k: K) {
const b = k in obj;
let k1: K;
for (k1 in obj) {
let x1 = obj[k1];
}
for (let k2 in obj) {
let x2 = obj[k2];
}
}

//// [keyofAndForIn.js]
// Repro from #12513
function f1(obj, k) {
var b = k in obj;
var k1;
for (k1 in obj) {
var x1 = obj[k1];
}
for (var k2 in obj) {
var x2 = obj[k2];
}
}
function f2(obj, k) {
var b = k in obj;
var k1;
for (k1 in obj) {
var x1 = obj[k1];
}
for (var k2 in obj) {
var x2 = obj[k2];
}
}
function f3(obj, k) {
var b = k in obj;
var k1;
for (k1 in obj) {
var x1 = obj[k1];
}
for (var k2 in obj) {
var x2 = obj[k2];
}
}


//// [keyofAndForIn.d.ts]
declare function f1<K extends string, T>(obj: {
[P in K]: T;
}, k: K): void;
declare function f2<T>(obj: {
[P in keyof T]: T[P];
}, k: keyof T): void;
declare function f3<T, K extends keyof T>(obj: {
[P in K]: T[P];
}, k: K): void;
125 changes: 125 additions & 0 deletions tests/baselines/reference/keyofAndForIn.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
=== tests/cases/conformance/types/keyof/keyofAndForIn.ts ===

// Repro from #12513

function f1<K extends string, T>(obj: { [P in K]: T }, k: K) {
>f1 : Symbol(f1, Decl(keyofAndForIn.ts, 0, 0))
>K : Symbol(K, Decl(keyofAndForIn.ts, 3, 12))
>T : Symbol(T, Decl(keyofAndForIn.ts, 3, 29))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 3, 33))
>P : Symbol(P, Decl(keyofAndForIn.ts, 3, 41))
>K : Symbol(K, Decl(keyofAndForIn.ts, 3, 12))
>T : Symbol(T, Decl(keyofAndForIn.ts, 3, 29))
>k : Symbol(k, Decl(keyofAndForIn.ts, 3, 54))
>K : Symbol(K, Decl(keyofAndForIn.ts, 3, 12))

const b = k in obj;
>b : Symbol(b, Decl(keyofAndForIn.ts, 4, 9))
>k : Symbol(k, Decl(keyofAndForIn.ts, 3, 54))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 3, 33))

let k1: K;
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 5, 7))
>K : Symbol(K, Decl(keyofAndForIn.ts, 3, 12))

for (k1 in obj) {
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 5, 7))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 3, 33))

let x1 = obj[k1];
>x1 : Symbol(x1, Decl(keyofAndForIn.ts, 7, 11))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 3, 33))
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 5, 7))
}
for (let k2 in obj) {
>k2 : Symbol(k2, Decl(keyofAndForIn.ts, 9, 12))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 3, 33))

let x2 = obj[k2];
>x2 : Symbol(x2, Decl(keyofAndForIn.ts, 10, 11))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 3, 33))
>k2 : Symbol(k2, Decl(keyofAndForIn.ts, 9, 12))
}
}

function f2<T>(obj: { [P in keyof T]: T[P] }, k: keyof T) {
>f2 : Symbol(f2, Decl(keyofAndForIn.ts, 12, 1))
>T : Symbol(T, Decl(keyofAndForIn.ts, 14, 12))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 14, 15))
>P : Symbol(P, Decl(keyofAndForIn.ts, 14, 23))
>T : Symbol(T, Decl(keyofAndForIn.ts, 14, 12))
>T : Symbol(T, Decl(keyofAndForIn.ts, 14, 12))
>P : Symbol(P, Decl(keyofAndForIn.ts, 14, 23))
>k : Symbol(k, Decl(keyofAndForIn.ts, 14, 45))
>T : Symbol(T, Decl(keyofAndForIn.ts, 14, 12))

const b = k in obj;
>b : Symbol(b, Decl(keyofAndForIn.ts, 15, 9))
>k : Symbol(k, Decl(keyofAndForIn.ts, 14, 45))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 14, 15))

let k1: keyof T;
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 16, 7))
>T : Symbol(T, Decl(keyofAndForIn.ts, 14, 12))

for (k1 in obj) {
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 16, 7))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 14, 15))

let x1 = obj[k1];
>x1 : Symbol(x1, Decl(keyofAndForIn.ts, 18, 11))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 14, 15))
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 16, 7))
}
for (let k2 in obj) {
>k2 : Symbol(k2, Decl(keyofAndForIn.ts, 20, 12))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 14, 15))

let x2 = obj[k2];
>x2 : Symbol(x2, Decl(keyofAndForIn.ts, 21, 11))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 14, 15))
>k2 : Symbol(k2, Decl(keyofAndForIn.ts, 20, 12))
}
}

function f3<T, K extends keyof T>(obj: { [P in K]: T[P] }, k: K) {
>f3 : Symbol(f3, Decl(keyofAndForIn.ts, 23, 1))
>T : Symbol(T, Decl(keyofAndForIn.ts, 25, 12))
>K : Symbol(K, Decl(keyofAndForIn.ts, 25, 14))
>T : Symbol(T, Decl(keyofAndForIn.ts, 25, 12))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 25, 34))
>P : Symbol(P, Decl(keyofAndForIn.ts, 25, 42))
>K : Symbol(K, Decl(keyofAndForIn.ts, 25, 14))
>T : Symbol(T, Decl(keyofAndForIn.ts, 25, 12))
>P : Symbol(P, Decl(keyofAndForIn.ts, 25, 42))
>k : Symbol(k, Decl(keyofAndForIn.ts, 25, 58))
>K : Symbol(K, Decl(keyofAndForIn.ts, 25, 14))

const b = k in obj;
>b : Symbol(b, Decl(keyofAndForIn.ts, 26, 9))
>k : Symbol(k, Decl(keyofAndForIn.ts, 25, 58))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 25, 34))

let k1: K;
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 27, 7))
>K : Symbol(K, Decl(keyofAndForIn.ts, 25, 14))

for (k1 in obj) {
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 27, 7))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 25, 34))

let x1 = obj[k1];
>x1 : Symbol(x1, Decl(keyofAndForIn.ts, 29, 11))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 25, 34))
>k1 : Symbol(k1, Decl(keyofAndForIn.ts, 27, 7))
}
for (let k2 in obj) {
>k2 : Symbol(k2, Decl(keyofAndForIn.ts, 31, 12))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 25, 34))

let x2 = obj[k2];
>x2 : Symbol(x2, Decl(keyofAndForIn.ts, 32, 11))
>obj : Symbol(obj, Decl(keyofAndForIn.ts, 25, 34))
>k2 : Symbol(k2, Decl(keyofAndForIn.ts, 31, 12))
}
}
Loading