Skip to content

Commit 0a1a3ec

Browse files
committed
Merge branch 'master' into mappedTypeInference
# Conflicts: # src/compiler/checker.ts
2 parents 36ad772 + 88b7d53 commit 0a1a3ec

15 files changed

+566
-52
lines changed

src/compiler/checker.ts

Lines changed: 46 additions & 39 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
}
@@ -8635,33 +8639,6 @@ namespace ts {
86358639
}
86368640
}
86378641
else {
8638-
if (getObjectFlags(target) & ObjectFlags.Mapped) {
8639-
const constraintType = getConstraintTypeFromMappedType(<MappedType>target);
8640-
if (getObjectFlags(source) & ObjectFlags.Mapped) {
8641-
// We're inferring from a mapped type to a mapped type, so simply infer from constraint type to
8642-
// constraint type and from template type to template type.
8643-
inferFromTypes(getConstraintTypeFromMappedType(<MappedType>source), constraintType);
8644-
inferFromTypes(getTemplateTypeFromMappedType(<MappedType>source), getTemplateTypeFromMappedType(<MappedType>target));
8645-
return;
8646-
}
8647-
if (constraintType.flags & TypeFlags.TypeParameter) {
8648-
// We're inferring from some source type S to a mapped type { [P in T]: X }, where T is a type
8649-
// parameter. Infer from 'keyof S' to T and infer from a union of each property type in S to X.
8650-
inferFromTypes(getIndexType(source), constraintType);
8651-
inferFromTypes(getUnionType(map(getPropertiesOfType(source), getTypeOfSymbol)), getTemplateTypeFromMappedType(<MappedType>target));
8652-
return;
8653-
}
8654-
if (constraintType.flags & TypeFlags.Index) {
8655-
// We're inferring from some source type S to an isomorphic mapped type { [P in keyof T]: X },
8656-
// where T is a type parameter. Use inferTypeForIsomorphicMappedType to infer a suitable source
8657-
// type and then infer from that type to T.
8658-
const index = indexOf(typeParameters, (<IndexType>constraintType).type);
8659-
if (index >= 0 && !typeInferences[index].isFixed) {
8660-
inferFromTypes(inferTypeForIsomorphicMappedType(source, <MappedType>target), typeParameters[index]);
8661-
}
8662-
return;
8663-
}
8664-
}
86658642
source = getApparentType(source);
86668643
if (source.flags & TypeFlags.Object) {
86678644
if (isInProcess(source, target)) {
@@ -8682,15 +8659,46 @@ namespace ts {
86828659
sourceStack[depth] = source;
86838660
targetStack[depth] = target;
86848661
depth++;
8685-
inferFromProperties(source, target);
8686-
inferFromSignatures(source, target, SignatureKind.Call);
8687-
inferFromSignatures(source, target, SignatureKind.Construct);
8688-
inferFromIndexTypes(source, target);
8662+
inferFromObjectTypes(source, target);
86898663
depth--;
86908664
}
86918665
}
86928666
}
86938667

8668+
function inferFromObjectTypes(source: Type, target: Type) {
8669+
if (getObjectFlags(target) & ObjectFlags.Mapped) {
8670+
const constraintType = getConstraintTypeFromMappedType(<MappedType>target);
8671+
if (getObjectFlags(source) & ObjectFlags.Mapped) {
8672+
// We're inferring from a mapped type to a mapped type, so simply infer from constraint type to
8673+
// constraint type and from template type to template type.
8674+
inferFromTypes(getConstraintTypeFromMappedType(<MappedType>source), constraintType);
8675+
inferFromTypes(getTemplateTypeFromMappedType(<MappedType>source), getTemplateTypeFromMappedType(<MappedType>target));
8676+
return;
8677+
}
8678+
if (constraintType.flags & TypeFlags.TypeParameter) {
8679+
// We're inferring from some source type S to a mapped type { [P in T]: X }, where T is a type
8680+
// parameter. Infer from 'keyof S' to T and infer from a union of each property type in S to X.
8681+
inferFromTypes(getIndexType(source), constraintType);
8682+
inferFromTypes(getUnionType(map(getPropertiesOfType(source), getTypeOfSymbol)), getTemplateTypeFromMappedType(<MappedType>target));
8683+
return;
8684+
}
8685+
if (constraintType.flags & TypeFlags.Index) {
8686+
// We're inferring from some source type S to an isomorphic mapped type { [P in keyof T]: X },
8687+
// where T is a type parameter. Use inferTypeForIsomorphicMappedType to infer a suitable source
8688+
// type and then infer from that type to T.
8689+
const index = indexOf(typeParameters, (<IndexType>constraintType).type);
8690+
if (index >= 0 && !typeInferences[index].isFixed) {
8691+
inferFromTypes(inferTypeForIsomorphicMappedType(source, <MappedType>target), typeParameters[index]);
8692+
}
8693+
return;
8694+
}
8695+
}
8696+
inferFromProperties(source, target);
8697+
inferFromSignatures(source, target, SignatureKind.Call);
8698+
inferFromSignatures(source, target, SignatureKind.Construct);
8699+
inferFromIndexTypes(source, target);
8700+
}
8701+
86948702
function inferFromProperties(source: Type, target: Type) {
86958703
const properties = getPropertiesOfObjectType(target);
86968704
for (const targetProp of properties) {
@@ -14338,7 +14346,7 @@ namespace ts {
1433814346
// The in operator requires the left operand to be of type Any, the String primitive type, or the Number primitive type,
1433914347
// and the right operand to be of type Any, an object type, or a type parameter type.
1434014348
// The result is always of the Boolean primitive type.
14341-
if (!isTypeAnyOrAllConstituentTypesHaveKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) {
14349+
if (!(isTypeComparableTo(leftType, stringType) || isTypeOfKind(leftType, TypeFlags.NumberLike | TypeFlags.ESSymbol))) {
1434214350
error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol);
1434314351
}
1434414352
if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeParameter | TypeFlags.IndexedAccess)) {
@@ -14933,7 +14941,7 @@ namespace ts {
1493314941
}
1493414942
contextualType = apparentType;
1493514943
}
14936-
return maybeTypeOfKind(contextualType, TypeFlags.Literal);
14944+
return maybeTypeOfKind(contextualType, (TypeFlags.Literal | TypeFlags.Index));
1493714945
}
1493814946
return false;
1493914947
}
@@ -17229,6 +17237,7 @@ namespace ts {
1722917237
// Grammar checking
1723017238
checkGrammarForInOrForOfStatement(node);
1723117239

17240+
const rightType = checkNonNullExpression(node.expression);
1723217241
// TypeScript 1.0 spec (April 2014): 5.4
1723317242
// In a 'for-in' statement of the form
1723417243
// for (let VarDecl in Expr) Statement
@@ -17239,7 +17248,6 @@ namespace ts {
1723917248
if (variable && isBindingPattern(variable.name)) {
1724017249
error(variable.name, Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern);
1724117250
}
17242-
1724317251
checkForInOrForOfVariableDeclaration(node);
1724417252
}
1724517253
else {
@@ -17252,7 +17260,7 @@ namespace ts {
1725217260
if (varExpr.kind === SyntaxKind.ArrayLiteralExpression || varExpr.kind === SyntaxKind.ObjectLiteralExpression) {
1725317261
error(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern);
1725417262
}
17255-
else if (!isTypeAnyOrAllConstituentTypesHaveKind(leftType, TypeFlags.StringLike)) {
17263+
else if (!isTypeAssignableTo(getIndexTypeOrString(rightType), leftType)) {
1725617264
error(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_of_type_string_or_any);
1725717265
}
1725817266
else {
@@ -17261,7 +17269,6 @@ namespace ts {
1726117269
}
1726217270
}
1726317271

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

src/compiler/transformers/ts.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,13 +1353,13 @@ namespace ts {
13531353
// __metadata("design:type", Function),
13541354
// __metadata("design:paramtypes", [Object]),
13551355
// __metadata("design:returntype", void 0)
1356-
// ], C.prototype, "method", undefined);
1356+
// ], C.prototype, "method", null);
13571357
//
13581358
// The emit for an accessor is:
13591359
//
13601360
// __decorate([
13611361
// dec
1362-
// ], C.prototype, "accessor", undefined);
1362+
// ], C.prototype, "accessor", null);
13631363
//
13641364
// The emit for a property is:
13651365
//

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;

0 commit comments

Comments
 (0)