@@ -6472,7 +6472,36 @@ namespace ts {
6472
6472
return result;
6473
6473
}
6474
6474
6475
- function instantiateMappedType(type: MappedType, mapper: TypeMapper): MappedType {
6475
+ function instantiateMappedType(type: MappedType, mapper: TypeMapper): Type {
6476
+ // Check if we have an isomorphic mapped type, i.e. a type of the form { [P in keyof T]: X } for some
6477
+ // type parameter T. If so, the mapped type is distributive over a union type and when T is instantiated
6478
+ // to a union type A | B, we produce { [P in keyof A]: X } | { [P in keyof B]: X }. Furthermore, for
6479
+ // isomorphic mapped types we leave primitive types alone. For example, when T is instantiated to a
6480
+ // union type A | undefined, we produce { [P in keyof A]: X } | undefined.
6481
+ const constraintType = getConstraintTypeFromMappedType(type);
6482
+ if (constraintType.flags & TypeFlags.Index) {
6483
+ const typeParameter = (<IndexType>constraintType).type;
6484
+ const mappedTypeParameter = mapper(typeParameter);
6485
+ if (typeParameter !== mappedTypeParameter) {
6486
+ return mapType(mappedTypeParameter, t => {
6487
+ if (isMappableType(t)) {
6488
+ const replacementMapper = createUnaryTypeMapper(typeParameter, t);
6489
+ const combinedMapper = mapper.mappedTypes && mapper.mappedTypes.length === 1 ? replacementMapper : combineTypeMappers(replacementMapper, mapper);
6490
+ combinedMapper.mappedTypes = mapper.mappedTypes;
6491
+ return instantiateMappedObjectType(type, combinedMapper);
6492
+ }
6493
+ return t;
6494
+ });
6495
+ }
6496
+ }
6497
+ return instantiateMappedObjectType(type, mapper);
6498
+ }
6499
+
6500
+ function isMappableType(type: Type) {
6501
+ return type.flags & (TypeFlags.TypeParameter | TypeFlags.Object | TypeFlags.Intersection | TypeFlags.IndexedAccess);
6502
+ }
6503
+
6504
+ function instantiateMappedObjectType(type: MappedType, mapper: TypeMapper): Type {
6476
6505
const result = <MappedType>createObjectType(ObjectFlags.Mapped | ObjectFlags.Instantiated, type.symbol);
6477
6506
result.declaration = type.declaration;
6478
6507
result.mapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper;
@@ -7514,25 +7543,30 @@ namespace ts {
7514
7543
7515
7544
// 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.
7516
7545
function mappedTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
7517
- if (isGenericMappedType(source) && isGenericMappedType(target)) {
7518
- let result: Ternary;
7519
- if (relation === identityRelation) {
7520
- const readonlyMatches = !(<MappedType>source).declaration.readonlyToken === !(<MappedType>target).declaration.readonlyToken;
7521
- const optionalMatches = !(<MappedType>source).declaration.questionToken === !(<MappedType>target).declaration.questionToken;
7522
- if (readonlyMatches && optionalMatches) {
7523
- if (result = isRelatedTo(getConstraintTypeFromMappedType(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) {
7524
- return result & isRelatedTo(getErasedTemplateTypeFromMappedType(<MappedType>source), getErasedTemplateTypeFromMappedType(<MappedType>target), reportErrors);
7546
+ if (isGenericMappedType(target)) {
7547
+ if (isGenericMappedType(source)) {
7548
+ let result: Ternary;
7549
+ if (relation === identityRelation) {
7550
+ const readonlyMatches = !(<MappedType>source).declaration.readonlyToken === !(<MappedType>target).declaration.readonlyToken;
7551
+ const optionalMatches = !(<MappedType>source).declaration.questionToken === !(<MappedType>target).declaration.questionToken;
7552
+ if (readonlyMatches && optionalMatches) {
7553
+ if (result = isRelatedTo(getConstraintTypeFromMappedType(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) {
7554
+ return result & isRelatedTo(getErasedTemplateTypeFromMappedType(<MappedType>source), getErasedTemplateTypeFromMappedType(<MappedType>target), reportErrors);
7555
+ }
7525
7556
}
7526
7557
}
7527
- }
7528
- else {
7529
- if (relation === comparableRelation || !( <MappedType>source).declaration.questionToken || (<MappedType>target).declaration.questionToken ) {
7530
- if ( result = isRelatedTo(getConstraintTypeFromMappedType (<MappedType>target ), getConstraintTypeFromMappedType (<MappedType>source ), reportErrors)) {
7531
- return result & isRelatedTo(getTemplateTypeFromMappedType(<MappedType>source), getTemplateTypeFromMappedType(<MappedType>target), reportErrors);
7558
+ else {
7559
+ if (relation === comparableRelation || !(<MappedType>source).declaration.questionToken || (<MappedType>target).declaration.questionToken) {
7560
+ if (result = isRelatedTo(getConstraintTypeFromMappedType( <MappedType>target), getConstraintTypeFromMappedType (<MappedType>source), reportErrors) ) {
7561
+ return result & isRelatedTo(getTemplateTypeFromMappedType (<MappedType>source ), getTemplateTypeFromMappedType (<MappedType>target ), reportErrors);
7562
+ }
7532
7563
}
7533
7564
}
7534
7565
}
7535
7566
}
7567
+ else if (relation !== identityRelation && isEmptyObjectType(resolveStructuredTypeMembers(<ObjectType>target))) {
7568
+ return Ternary.True;
7569
+ }
7536
7570
return Ternary.False;
7537
7571
}
7538
7572
0 commit comments