diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f7d01d5480d..fcf90a11da2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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((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) { @@ -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) { @@ -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(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; } @@ -8635,33 +8639,6 @@ namespace ts { } } else { - if (getObjectFlags(target) & ObjectFlags.Mapped) { - const constraintType = getConstraintTypeFromMappedType(target); - if (getObjectFlags(source) & ObjectFlags.Mapped) { - // We're inferring from a mapped type to a mapped type, so simply infer from constraint type to - // constraint type and from template type to template type. - inferFromTypes(getConstraintTypeFromMappedType(source), constraintType); - inferFromTypes(getTemplateTypeFromMappedType(source), getTemplateTypeFromMappedType(target)); - return; - } - if (constraintType.flags & TypeFlags.TypeParameter) { - // We're inferring from some source type S to a mapped type { [P in T]: X }, where T is a type - // parameter. Infer from 'keyof S' to T and infer from a union of each property type in S to X. - inferFromTypes(getIndexType(source), constraintType); - inferFromTypes(getUnionType(map(getPropertiesOfType(source), getTypeOfSymbol)), getTemplateTypeFromMappedType(target)); - return; - } - if (constraintType.flags & TypeFlags.Index) { - // We're inferring from some source type S to an isomorphic mapped type { [P in keyof T]: X }, - // where T is a type parameter. Use inferTypeForIsomorphicMappedType to infer a suitable source - // type and then infer from that type to T. - const index = indexOf(typeParameters, (constraintType).type); - if (index >= 0 && !typeInferences[index].isFixed) { - inferFromTypes(inferTypeForIsomorphicMappedType(source, target), typeParameters[index]); - } - return; - } - } source = getApparentType(source); if (source.flags & TypeFlags.Object) { if (isInProcess(source, target)) { @@ -8682,15 +8659,46 @@ namespace ts { sourceStack[depth] = source; targetStack[depth] = target; depth++; - inferFromProperties(source, target); - inferFromSignatures(source, target, SignatureKind.Call); - inferFromSignatures(source, target, SignatureKind.Construct); - inferFromIndexTypes(source, target); + inferFromObjectTypes(source, target); depth--; } } } + function inferFromObjectTypes(source: Type, target: Type) { + if (getObjectFlags(target) & ObjectFlags.Mapped) { + const constraintType = getConstraintTypeFromMappedType(target); + if (getObjectFlags(source) & ObjectFlags.Mapped) { + // We're inferring from a mapped type to a mapped type, so simply infer from constraint type to + // constraint type and from template type to template type. + inferFromTypes(getConstraintTypeFromMappedType(source), constraintType); + inferFromTypes(getTemplateTypeFromMappedType(source), getTemplateTypeFromMappedType(target)); + return; + } + if (constraintType.flags & TypeFlags.TypeParameter) { + // We're inferring from some source type S to a mapped type { [P in T]: X }, where T is a type + // parameter. Infer from 'keyof S' to T and infer from a union of each property type in S to X. + inferFromTypes(getIndexType(source), constraintType); + inferFromTypes(getUnionType(map(getPropertiesOfType(source), getTypeOfSymbol)), getTemplateTypeFromMappedType(target)); + return; + } + if (constraintType.flags & TypeFlags.Index) { + // We're inferring from some source type S to an isomorphic mapped type { [P in keyof T]: X }, + // where T is a type parameter. Use inferTypeForIsomorphicMappedType to infer a suitable source + // type and then infer from that type to T. + const index = indexOf(typeParameters, (constraintType).type); + if (index >= 0 && !typeInferences[index].isFixed) { + inferFromTypes(inferTypeForIsomorphicMappedType(source, target), typeParameters[index]); + } + return; + } + } + inferFromProperties(source, target); + inferFromSignatures(source, target, SignatureKind.Call); + inferFromSignatures(source, target, SignatureKind.Construct); + inferFromIndexTypes(source, target); + } + function inferFromProperties(source: Type, target: Type) { const properties = getPropertiesOfObjectType(target); for (const targetProp of properties) { @@ -14338,7 +14346,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)) { @@ -14933,7 +14941,7 @@ namespace ts { } contextualType = apparentType; } - return maybeTypeOfKind(contextualType, TypeFlags.Literal); + return maybeTypeOfKind(contextualType, (TypeFlags.Literal | TypeFlags.Index)); } return false; } @@ -17229,6 +17237,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 @@ -17239,7 +17248,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 { @@ -17252,7 +17260,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 { @@ -17261,7 +17269,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)) { diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 62b2022b7df..52717a94996 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1353,13 +1353,13 @@ namespace ts { // __metadata("design:type", Function), // __metadata("design:paramtypes", [Object]), // __metadata("design:returntype", void 0) - // ], C.prototype, "method", undefined); + // ], C.prototype, "method", null); // // The emit for an accessor is: // // __decorate([ // dec - // ], C.prototype, "accessor", undefined); + // ], C.prototype, "accessor", null); // // The emit for a property is: // diff --git a/tests/baselines/reference/inOperatorWithInvalidOperands.errors.txt b/tests/baselines/reference/inOperatorWithInvalidOperands.errors.txt index 0985157e0da..5aeae5f160e 100644 --- a/tests/baselines/reference/inOperatorWithInvalidOperands.errors.txt +++ b/tests/baselines/reference/inOperatorWithInvalidOperands.errors.txt @@ -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 @@ -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; @@ -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; ~~~~~ diff --git a/tests/baselines/reference/keyofAndForIn.js b/tests/baselines/reference/keyofAndForIn.js new file mode 100644 index 00000000000..0debf28b75e --- /dev/null +++ b/tests/baselines/reference/keyofAndForIn.js @@ -0,0 +1,81 @@ +//// [keyofAndForIn.ts] + +// Repro from #12513 + +function f1(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(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(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(obj: { + [P in K]: T; +}, k: K): void; +declare function f2(obj: { + [P in keyof T]: T[P]; +}, k: keyof T): void; +declare function f3(obj: { + [P in K]: T[P]; +}, k: K): void; diff --git a/tests/baselines/reference/keyofAndForIn.symbols b/tests/baselines/reference/keyofAndForIn.symbols new file mode 100644 index 00000000000..36b5d3e2d72 --- /dev/null +++ b/tests/baselines/reference/keyofAndForIn.symbols @@ -0,0 +1,125 @@ +=== tests/cases/conformance/types/keyof/keyofAndForIn.ts === + +// Repro from #12513 + +function f1(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(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(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)) + } +} diff --git a/tests/baselines/reference/keyofAndForIn.types b/tests/baselines/reference/keyofAndForIn.types new file mode 100644 index 00000000000..5e992e5d261 --- /dev/null +++ b/tests/baselines/reference/keyofAndForIn.types @@ -0,0 +1,134 @@ +=== tests/cases/conformance/types/keyof/keyofAndForIn.ts === + +// Repro from #12513 + +function f1(obj: { [P in K]: T }, k: K) { +>f1 : (obj: { [P in K]: T; }, k: K) => void +>K : K +>T : T +>obj : { [P in K]: T; } +>P : P +>K : K +>T : T +>k : K +>K : K + + const b = k in obj; +>b : boolean +>k in obj : boolean +>k : K +>obj : { [P in K]: T; } + + let k1: K; +>k1 : K +>K : K + + for (k1 in obj) { +>k1 : K +>obj : { [P in K]: T; } + + let x1 = obj[k1]; +>x1 : T +>obj[k1] : T +>obj : { [P in K]: T; } +>k1 : K + } + for (let k2 in obj) { +>k2 : K +>obj : { [P in K]: T; } + + let x2 = obj[k2]; +>x2 : T +>obj[k2] : T +>obj : { [P in K]: T; } +>k2 : K + } +} + +function f2(obj: { [P in keyof T]: T[P] }, k: keyof T) { +>f2 : (obj: { [P in keyof T]: T[P]; }, k: keyof T) => void +>T : T +>obj : { [P in keyof T]: T[P]; } +>P : P +>T : T +>T : T +>P : P +>k : keyof T +>T : T + + const b = k in obj; +>b : boolean +>k in obj : boolean +>k : keyof T +>obj : { [P in keyof T]: T[P]; } + + let k1: keyof T; +>k1 : keyof T +>T : T + + for (k1 in obj) { +>k1 : keyof T +>obj : { [P in keyof T]: T[P]; } + + let x1 = obj[k1]; +>x1 : T[keyof T] +>obj[k1] : T[keyof T] +>obj : { [P in keyof T]: T[P]; } +>k1 : keyof T + } + for (let k2 in obj) { +>k2 : keyof T +>obj : { [P in keyof T]: T[P]; } + + let x2 = obj[k2]; +>x2 : T[keyof T] +>obj[k2] : T[keyof T] +>obj : { [P in keyof T]: T[P]; } +>k2 : keyof T + } +} + +function f3(obj: { [P in K]: T[P] }, k: K) { +>f3 : (obj: { [P in K]: T[P]; }, k: K) => void +>T : T +>K : K +>T : T +>obj : { [P in K]: T[P]; } +>P : P +>K : K +>T : T +>P : P +>k : K +>K : K + + const b = k in obj; +>b : boolean +>k in obj : boolean +>k : K +>obj : { [P in K]: T[P]; } + + let k1: K; +>k1 : K +>K : K + + for (k1 in obj) { +>k1 : K +>obj : { [P in K]: T[P]; } + + let x1 = obj[k1]; +>x1 : T[K] +>obj[k1] : T[K] +>obj : { [P in K]: T[P]; } +>k1 : K + } + for (let k2 in obj) { +>k2 : K +>obj : { [P in K]: T[P]; } + + let x2 = obj[k2]; +>x2 : T[K] +>obj[k2] : T[K] +>obj : { [P in K]: T[P]; } +>k2 : K + } +} diff --git a/tests/baselines/reference/keyofIsLiteralContexualType.errors.txt b/tests/baselines/reference/keyofIsLiteralContexualType.errors.txt new file mode 100644 index 00000000000..8424d528b22 --- /dev/null +++ b/tests/baselines/reference/keyofIsLiteralContexualType.errors.txt @@ -0,0 +1,32 @@ +tests/cases/compiler/keyofIsLiteralContexualType.ts(5,9): error TS2322: Type '("a" | "b" | "c")[]' is not assignable to type 'keyof T[]'. + Type '"a" | "b" | "c"' is not assignable to type 'keyof T'. + Type '"a" | "b" | "c"' is not assignable to type '"a" | "b"'. + Type '"c"' is not assignable to type '"a" | "b"'. + Type '"c"' is not assignable to type 'keyof T'. + Type '"c"' is not assignable to type '"a" | "b"'. +tests/cases/compiler/keyofIsLiteralContexualType.ts(13,11): error TS2339: Property 'b' does not exist on type 'Pick<{ a: number; b: number; c: number; }, "a" | "c">'. + + +==== tests/cases/compiler/keyofIsLiteralContexualType.ts (2 errors) ==== + // keyof T is a literal contextual type + + function foo() { + let a: (keyof T)[] = ["a", "b"]; + let b: (keyof T)[] = ["a", "b", "c"]; + ~ +!!! error TS2322: Type '("a" | "b" | "c")[]' is not assignable to type 'keyof T[]'. +!!! error TS2322: Type '"a" | "b" | "c"' is not assignable to type 'keyof T'. +!!! error TS2322: Type '"a" | "b" | "c"' is not assignable to type '"a" | "b"'. +!!! error TS2322: Type '"c"' is not assignable to type '"a" | "b"'. +!!! error TS2322: Type '"c"' is not assignable to type 'keyof T'. +!!! error TS2322: Type '"c"' is not assignable to type '"a" | "b"'. + } + + // Repro from #12455 + + declare function pick(obj: T, propNames: K[]): Pick; + + let x = pick({ a: 10, b: 20, c: 30 }, ["a", "c"]); + let b = x.b; // Error + ~ +!!! error TS2339: Property 'b' does not exist on type 'Pick<{ a: number; b: number; c: number; }, "a" | "c">'. \ No newline at end of file diff --git a/tests/baselines/reference/keyofIsLiteralContexualType.js b/tests/baselines/reference/keyofIsLiteralContexualType.js new file mode 100644 index 00000000000..5325b976174 --- /dev/null +++ b/tests/baselines/reference/keyofIsLiteralContexualType.js @@ -0,0 +1,23 @@ +//// [keyofIsLiteralContexualType.ts] +// keyof T is a literal contextual type + +function foo() { + let a: (keyof T)[] = ["a", "b"]; + let b: (keyof T)[] = ["a", "b", "c"]; +} + +// Repro from #12455 + +declare function pick(obj: T, propNames: K[]): Pick; + +let x = pick({ a: 10, b: 20, c: 30 }, ["a", "c"]); +let b = x.b; // Error + +//// [keyofIsLiteralContexualType.js] +// keyof T is a literal contextual type +function foo() { + var a = ["a", "b"]; + var b = ["a", "b", "c"]; +} +var x = pick({ a: 10, b: 20, c: 30 }, ["a", "c"]); +var b = x.b; // Error diff --git a/tests/baselines/reference/mappedTypeInferenceCircularity.js b/tests/baselines/reference/mappedTypeInferenceCircularity.js new file mode 100644 index 00000000000..d5918d342f6 --- /dev/null +++ b/tests/baselines/reference/mappedTypeInferenceCircularity.js @@ -0,0 +1,12 @@ +//// [mappedTypeInferenceCircularity.ts] +// Repro from #12511 + +type HTML = { [K in 'div']: Block }; +type Block

= (func: HTML) => {}; + +declare var h: HTML; +h.div(h); + +//// [mappedTypeInferenceCircularity.js] +// Repro from #12511 +h.div(h); diff --git a/tests/baselines/reference/mappedTypeInferenceCircularity.symbols b/tests/baselines/reference/mappedTypeInferenceCircularity.symbols new file mode 100644 index 00000000000..33dceb5cd76 --- /dev/null +++ b/tests/baselines/reference/mappedTypeInferenceCircularity.symbols @@ -0,0 +1,26 @@ +=== tests/cases/compiler/mappedTypeInferenceCircularity.ts === +// Repro from #12511 + +type HTML = { [K in 'div']: Block }; +>HTML : Symbol(HTML, Decl(mappedTypeInferenceCircularity.ts, 0, 0)) +>K : Symbol(K, Decl(mappedTypeInferenceCircularity.ts, 2, 15)) +>Block : Symbol(Block, Decl(mappedTypeInferenceCircularity.ts, 2, 42)) +>HTML : Symbol(HTML, Decl(mappedTypeInferenceCircularity.ts, 0, 0)) + +type Block

= (func: HTML) => {}; +>Block : Symbol(Block, Decl(mappedTypeInferenceCircularity.ts, 2, 42)) +>P : Symbol(P, Decl(mappedTypeInferenceCircularity.ts, 3, 11)) +>T : Symbol(T, Decl(mappedTypeInferenceCircularity.ts, 3, 17)) +>func : Symbol(func, Decl(mappedTypeInferenceCircularity.ts, 3, 20)) +>HTML : Symbol(HTML, Decl(mappedTypeInferenceCircularity.ts, 0, 0)) + +declare var h: HTML; +>h : Symbol(h, Decl(mappedTypeInferenceCircularity.ts, 5, 11)) +>HTML : Symbol(HTML, Decl(mappedTypeInferenceCircularity.ts, 0, 0)) + +h.div(h); +>h.div : Symbol(div) +>h : Symbol(h, Decl(mappedTypeInferenceCircularity.ts, 5, 11)) +>div : Symbol(div) +>h : Symbol(h, Decl(mappedTypeInferenceCircularity.ts, 5, 11)) + diff --git a/tests/baselines/reference/mappedTypeInferenceCircularity.types b/tests/baselines/reference/mappedTypeInferenceCircularity.types new file mode 100644 index 00000000000..451da474756 --- /dev/null +++ b/tests/baselines/reference/mappedTypeInferenceCircularity.types @@ -0,0 +1,27 @@ +=== tests/cases/compiler/mappedTypeInferenceCircularity.ts === +// Repro from #12511 + +type HTML = { [K in 'div']: Block }; +>HTML : HTML +>K : K +>Block : Block

+>HTML : HTML + +type Block

= (func: HTML) => {}; +>Block : Block

+>P : P +>T : T +>func : HTML +>HTML : HTML + +declare var h: HTML; +>h : HTML +>HTML : HTML + +h.div(h); +>h.div(h) : {} +>h.div : Block +>h : HTML +>div : Block +>h : HTML + diff --git a/tests/baselines/reference/widenedTypes.errors.txt b/tests/baselines/reference/widenedTypes.errors.txt index ea7c2b2f92a..5ea097acf2e 100644 --- a/tests/baselines/reference/widenedTypes.errors.txt +++ b/tests/baselines/reference/widenedTypes.errors.txt @@ -1,5 +1,4 @@ tests/cases/compiler/widenedTypes.ts(2,1): error TS2358: The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter. -tests/cases/compiler/widenedTypes.ts(5,1): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. tests/cases/compiler/widenedTypes.ts(6,7): error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter tests/cases/compiler/widenedTypes.ts(8,15): error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter. tests/cases/compiler/widenedTypes.ts(10,14): error TS2695: Left side of comma operator is unused and has no side effects. @@ -12,7 +11,7 @@ tests/cases/compiler/widenedTypes.ts(24,5): error TS2322: Type '{ x: number; y: Type 'number' is not assignable to type 'string'. -==== tests/cases/compiler/widenedTypes.ts (9 errors) ==== +==== tests/cases/compiler/widenedTypes.ts (8 errors) ==== null instanceof (() => { }); ~~~~ @@ -20,8 +19,6 @@ tests/cases/compiler/widenedTypes.ts(24,5): error TS2322: Type '{ x: number; y: ({}) instanceof null; // Ok because null is a subtype of function null in {}; - ~~~~ -!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. "" in null; ~~~~ !!! error TS2361: The right-hand side of an 'in' expression must be of type 'any', an object type or a type parameter diff --git a/tests/cases/compiler/keyofIsLiteralContexualType.ts b/tests/cases/compiler/keyofIsLiteralContexualType.ts new file mode 100644 index 00000000000..dd14de92289 --- /dev/null +++ b/tests/cases/compiler/keyofIsLiteralContexualType.ts @@ -0,0 +1,13 @@ +// keyof T is a literal contextual type + +function foo() { + let a: (keyof T)[] = ["a", "b"]; + let b: (keyof T)[] = ["a", "b", "c"]; +} + +// Repro from #12455 + +declare function pick(obj: T, propNames: K[]): Pick; + +let x = pick({ a: 10, b: 20, c: 30 }, ["a", "c"]); +let b = x.b; // Error \ No newline at end of file diff --git a/tests/cases/compiler/mappedTypeInferenceCircularity.ts b/tests/cases/compiler/mappedTypeInferenceCircularity.ts new file mode 100644 index 00000000000..56fe08c5fe2 --- /dev/null +++ b/tests/cases/compiler/mappedTypeInferenceCircularity.ts @@ -0,0 +1,7 @@ +// Repro from #12511 + +type HTML = { [K in 'div']: Block }; +type Block

= (func: HTML) => {}; + +declare var h: HTML; +h.div(h); \ No newline at end of file diff --git a/tests/cases/conformance/types/keyof/keyofAndForIn.ts b/tests/cases/conformance/types/keyof/keyofAndForIn.ts new file mode 100644 index 00000000000..97b8587a243 --- /dev/null +++ b/tests/cases/conformance/types/keyof/keyofAndForIn.ts @@ -0,0 +1,36 @@ +// @declaration: true + +// Repro from #12513 + +function f1(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(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(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]; + } +} \ No newline at end of file