@@ -4550,6 +4550,10 @@ namespace ts {
4550
4550
unknownType);
4551
4551
}
4552
4552
4553
+ function getErasedTemplateTypeFromMappedType(type: MappedType) {
4554
+ return instantiateType(getTemplateTypeFromMappedType(type), createUnaryTypeMapper(getTypeParameterFromMappedType(type), anyType));
4555
+ }
4556
+
4553
4557
function isGenericMappedType(type: Type) {
4554
4558
if (getObjectFlags(type) & ObjectFlags.Mapped) {
4555
4559
const constraintType = getConstraintTypeFromMappedType(<MappedType>type);
@@ -7190,29 +7194,18 @@ namespace ts {
7190
7194
return result;
7191
7195
}
7192
7196
}
7193
- if (isGenericMappedType(target)) {
7194
- // A type [P in S]: X is related to a type [P in T]: Y if T is related to S and X is related to Y.
7195
- if (isGenericMappedType(source)) {
7196
- if ((result = isRelatedTo(getConstraintTypeFromMappedType(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) &&
7197
- (result = isRelatedTo(getTemplateTypeFromMappedType(<MappedType>source), getTemplateTypeFromMappedType(<MappedType>target), reportErrors))) {
7198
- return result;
7199
- }
7200
- }
7201
- }
7202
- else {
7203
- // Even if relationship doesn't hold for unions, intersections, or generic type references,
7204
- // it may hold in a structural comparison.
7205
- const apparentSource = getApparentType(source);
7206
- // In a check of the form X = A & B, we will have previously checked if A relates to X or B relates
7207
- // to X. Failing both of those we want to check if the aggregation of A and B's members structurally
7208
- // relates to X. Thus, we include intersection types on the source side here.
7209
- if (apparentSource.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) {
7210
- // Report structural errors only if we haven't reported any errors yet
7211
- const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo && !(source.flags & TypeFlags.Primitive);
7212
- if (result = objectTypeRelatedTo(apparentSource, source, target, reportStructuralErrors)) {
7213
- errorInfo = saveErrorInfo;
7214
- return result;
7215
- }
7197
+ // Even if relationship doesn't hold for unions, intersections, or generic type references,
7198
+ // it may hold in a structural comparison.
7199
+ const apparentSource = getApparentType(source);
7200
+ // In a check of the form X = A & B, we will have previously checked if A relates to X or B relates
7201
+ // to X. Failing both of those we want to check if the aggregation of A and B's members structurally
7202
+ // relates to X. Thus, we include intersection types on the source side here.
7203
+ if (apparentSource.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) {
7204
+ // Report structural errors only if we haven't reported any errors yet
7205
+ const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo && !(source.flags & TypeFlags.Primitive);
7206
+ if (result = objectTypeRelatedTo(apparentSource, source, target, reportStructuralErrors)) {
7207
+ errorInfo = saveErrorInfo;
7208
+ return result;
7216
7209
}
7217
7210
}
7218
7211
}
@@ -7441,6 +7434,9 @@ namespace ts {
7441
7434
if (expandingFlags === 3) {
7442
7435
result = Ternary.Maybe;
7443
7436
}
7437
+ else if (isGenericMappedType(source) || isGenericMappedType(target)) {
7438
+ result = mappedTypeRelatedTo(source, target, reportErrors);
7439
+ }
7444
7440
else {
7445
7441
result = propertiesRelatedTo(source, target, reportErrors);
7446
7442
if (result) {
@@ -7472,6 +7468,30 @@ namespace ts {
7472
7468
return result;
7473
7469
}
7474
7470
7471
+ // A type [P in S]: X is related to a type [P in T]: Y if T is related to S and X is related to Y.
7472
+ function mappedTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
7473
+ if (isGenericMappedType(source) && isGenericMappedType(target)) {
7474
+ let result: Ternary;
7475
+ if (relation === identityRelation) {
7476
+ const readonlyMatches = !(<MappedType>source).declaration.readonlyToken === !(<MappedType>target).declaration.readonlyToken;
7477
+ const optionalMatches = !(<MappedType>source).declaration.questionToken === !(<MappedType>target).declaration.questionToken;
7478
+ if (readonlyMatches && optionalMatches) {
7479
+ if (result = isRelatedTo(getConstraintTypeFromMappedType(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) {
7480
+ return result & isRelatedTo(getErasedTemplateTypeFromMappedType(<MappedType>source), getErasedTemplateTypeFromMappedType(<MappedType>target), reportErrors);
7481
+ }
7482
+ }
7483
+ }
7484
+ else {
7485
+ if (relation === comparableRelation || !(<MappedType>source).declaration.questionToken || (<MappedType>target).declaration.questionToken) {
7486
+ if (result = isRelatedTo(getConstraintTypeFromMappedType(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) {
7487
+ return result & isRelatedTo(getTemplateTypeFromMappedType(<MappedType>source), getTemplateTypeFromMappedType(<MappedType>target), reportErrors);
7488
+ }
7489
+ }
7490
+ }
7491
+ }
7492
+ return Ternary.False;
7493
+ }
7494
+
7475
7495
function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
7476
7496
if (relation === identityRelation) {
7477
7497
return propertiesIdenticalTo(source, target);
@@ -9721,20 +9741,20 @@ namespace ts {
9721
9741
}
9722
9742
9723
9743
if (targetType) {
9724
- return getNarrowedType(type, targetType, assumeTrue);
9744
+ return getNarrowedType(type, targetType, assumeTrue, isTypeInstanceOf );
9725
9745
}
9726
9746
9727
9747
return type;
9728
9748
}
9729
9749
9730
- function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean) {
9750
+ function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, isRelated: (source: Type, target: Type) => boolean ) {
9731
9751
if (!assumeTrue) {
9732
- return filterType(type, t => !isTypeInstanceOf (t, candidate));
9752
+ return filterType(type, t => !isRelated (t, candidate));
9733
9753
}
9734
9754
// If the current type is a union type, remove all constituents that couldn't be instances of
9735
9755
// the candidate type. If one or more constituents remain, return a union of those.
9736
9756
if (type.flags & TypeFlags.Union) {
9737
- const assignableType = filterType(type, t => isTypeInstanceOf (t, candidate));
9757
+ const assignableType = filterType(type, t => isRelated (t, candidate));
9738
9758
if (!(assignableType.flags & TypeFlags.Never)) {
9739
9759
return assignableType;
9740
9760
}
@@ -9770,7 +9790,7 @@ namespace ts {
9770
9790
const predicateArgument = callExpression.arguments[predicate.parameterIndex];
9771
9791
if (predicateArgument) {
9772
9792
if (isMatchingReference(reference, predicateArgument)) {
9773
- return getNarrowedType(type, predicate.type, assumeTrue);
9793
+ return getNarrowedType(type, predicate.type, assumeTrue, isTypeSubtypeOf );
9774
9794
}
9775
9795
if (containsMatchingReference(reference, predicateArgument)) {
9776
9796
return declaredType;
@@ -9783,7 +9803,7 @@ namespace ts {
9783
9803
const accessExpression = invokedExpression as ElementAccessExpression | PropertyAccessExpression;
9784
9804
const possibleReference = skipParentheses(accessExpression.expression);
9785
9805
if (isMatchingReference(reference, possibleReference)) {
9786
- return getNarrowedType(type, predicate.type, assumeTrue);
9806
+ return getNarrowedType(type, predicate.type, assumeTrue, isTypeSubtypeOf );
9787
9807
}
9788
9808
if (containsMatchingReference(reference, possibleReference)) {
9789
9809
return declaredType;
0 commit comments