@@ -8425,7 +8425,7 @@ namespace ts {
8425
8425
// results for union and intersection types for performance reasons.
8426
8426
function couldContainTypeParameters(type: Type): boolean {
8427
8427
const objectFlags = getObjectFlags(type);
8428
- return !!(type.flags & TypeFlags.TypeParameter ||
8428
+ return !!(type.flags & ( TypeFlags.TypeParameter | TypeFlags.IndexedAccess) ||
8429
8429
objectFlags & ObjectFlags.Reference && forEach((<TypeReference>type).typeArguments, couldContainTypeParameters) ||
8430
8430
objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) ||
8431
8431
objectFlags & ObjectFlags.Mapped ||
@@ -8443,8 +8443,57 @@ namespace ts {
8443
8443
return type === typeParameter || type.flags & TypeFlags.UnionOrIntersection && forEach((<UnionOrIntersectionType>type).types, t => isTypeParameterAtTopLevel(t, typeParameter));
8444
8444
}
8445
8445
8446
- function inferTypes(context: InferenceContext, originalSource: Type, originalTarget: Type) {
8447
- const typeParameters = context.signature.typeParameters;
8446
+ // Infer a suitable input type for an isomorphic mapped type { [P in keyof T]: X }. We construct
8447
+ // an object type with the same set of properties as the source type, where the type of each
8448
+ // property is computed by inferring from the source property type to X for a synthetic type
8449
+ // parameter T[P] (i.e. we treat the type T[P] as the type parameter we're inferring for).
8450
+ function inferTypeForIsomorphicMappedType(source: Type, target: MappedType): Type {
8451
+ if (!isMappableType(source)) {
8452
+ return source;
8453
+ }
8454
+ const typeParameter = getIndexedAccessType((<IndexType>getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target));
8455
+ const typeParameterArray = [typeParameter];
8456
+ const typeInferences = createTypeInferencesObject();
8457
+ const typeInferencesArray = [typeInferences];
8458
+ const templateType = getTemplateTypeFromMappedType(target);
8459
+ const properties = getPropertiesOfType(source);
8460
+ const members = createSymbolTable(properties);
8461
+ let hasInferredTypes = false;
8462
+ for (const prop of properties) {
8463
+ const inferredPropType = inferTargetType(getTypeOfSymbol(prop));
8464
+ if (inferredPropType) {
8465
+ const inferredProp = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | prop.flags & SymbolFlags.Optional, prop.name);
8466
+ inferredProp.declarations = prop.declarations;
8467
+ inferredProp.type = inferredPropType;
8468
+ inferredProp.isReadonly = isReadonlySymbol(prop);
8469
+ members[prop.name] = inferredProp;
8470
+ hasInferredTypes = true;
8471
+ }
8472
+ }
8473
+ let indexInfo = getIndexInfoOfType(source, IndexKind.String);
8474
+ if (indexInfo) {
8475
+ const inferredIndexType = inferTargetType(indexInfo.type);
8476
+ if (inferredIndexType) {
8477
+ indexInfo = createIndexInfo(inferredIndexType, indexInfo.isReadonly);
8478
+ hasInferredTypes = true;
8479
+ }
8480
+ }
8481
+ return hasInferredTypes ? createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined) : source;
8482
+
8483
+ function inferTargetType(sourceType: Type): Type {
8484
+ typeInferences.primary = undefined;
8485
+ typeInferences.secondary = undefined;
8486
+ inferTypes(typeParameterArray, typeInferencesArray, sourceType, templateType);
8487
+ const inferences = typeInferences.primary || typeInferences.secondary;
8488
+ return inferences && getUnionType(inferences, /*subtypeReduction*/ true);
8489
+ }
8490
+ }
8491
+
8492
+ function inferTypesWithContext(context: InferenceContext, originalSource: Type, originalTarget: Type) {
8493
+ inferTypes(context.signature.typeParameters, context.inferences, originalSource, originalTarget);
8494
+ }
8495
+
8496
+ function inferTypes(typeParameters: Type[], typeInferences: TypeInferences[], originalSource: Type, originalTarget: Type) {
8448
8497
let sourceStack: Type[];
8449
8498
let targetStack: Type[];
8450
8499
let depth = 0;
@@ -8512,7 +8561,7 @@ namespace ts {
8512
8561
target = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>target, matchingTypes);
8513
8562
}
8514
8563
}
8515
- if (target.flags & TypeFlags.TypeParameter) {
8564
+ if (target.flags & ( TypeFlags.TypeParameter | TypeFlags.IndexedAccess) ) {
8516
8565
// If target is a type parameter, make an inference, unless the source type contains
8517
8566
// the anyFunctionType (the wildcard type that's used to avoid contextually typing functions).
8518
8567
// Because the anyFunctionType is internal, it should not be exposed to the user by adding
@@ -8524,7 +8573,7 @@ namespace ts {
8524
8573
}
8525
8574
for (let i = 0; i < typeParameters.length; i++) {
8526
8575
if (target === typeParameters[i]) {
8527
- const inferences = context.inferences [i];
8576
+ const inferences = typeInferences [i];
8528
8577
if (!inferences.isFixed) {
8529
8578
// Any inferences that are made to a type parameter in a union type are inferior
8530
8579
// to inferences made to a flat (non-union) type. This is because if we infer to
@@ -8538,7 +8587,7 @@ namespace ts {
8538
8587
if (!contains(candidates, source)) {
8539
8588
candidates.push(source);
8540
8589
}
8541
- if (!isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
8590
+ if (target.flags & TypeFlags.TypeParameter && !isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
8542
8591
inferences.topLevel = false;
8543
8592
}
8544
8593
}
@@ -8589,15 +8638,29 @@ namespace ts {
8589
8638
if (getObjectFlags(target) & ObjectFlags.Mapped) {
8590
8639
const constraintType = getConstraintTypeFromMappedType(<MappedType>target);
8591
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.
8592
8643
inferFromTypes(getConstraintTypeFromMappedType(<MappedType>source), constraintType);
8593
8644
inferFromTypes(getTemplateTypeFromMappedType(<MappedType>source), getTemplateTypeFromMappedType(<MappedType>target));
8594
8645
return;
8595
8646
}
8596
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.
8597
8650
inferFromTypes(getIndexType(source), constraintType);
8598
8651
inferFromTypes(getUnionType(map(getPropertiesOfType(source), getTypeOfSymbol)), getTemplateTypeFromMappedType(<MappedType>target));
8599
8652
return;
8600
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
+ }
8601
8664
}
8602
8665
source = getApparentType(source);
8603
8666
if (source.flags & TypeFlags.Object) {
@@ -12458,7 +12521,7 @@ namespace ts {
12458
12521
const context = createInferenceContext(signature, /*inferUnionTypes*/ true);
12459
12522
forEachMatchingParameterType(contextualSignature, signature, (source, target) => {
12460
12523
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
12461
- inferTypes (context, instantiateType(source, contextualMapper), target);
12524
+ inferTypesWithContext (context, instantiateType(source, contextualMapper), target);
12462
12525
});
12463
12526
return getSignatureInstantiation(signature, getInferredTypes(context));
12464
12527
}
@@ -12493,7 +12556,7 @@ namespace ts {
12493
12556
if (thisType) {
12494
12557
const thisArgumentNode = getThisArgumentOfCall(node);
12495
12558
const thisArgumentType = thisArgumentNode ? checkExpression(thisArgumentNode) : voidType;
12496
- inferTypes (context, thisArgumentType, thisType);
12559
+ inferTypesWithContext (context, thisArgumentType, thisType);
12497
12560
}
12498
12561
12499
12562
// We perform two passes over the arguments. In the first pass we infer from all arguments, but use
@@ -12515,7 +12578,7 @@ namespace ts {
12515
12578
argType = checkExpressionWithContextualType(arg, paramType, mapper);
12516
12579
}
12517
12580
12518
- inferTypes (context, argType, paramType);
12581
+ inferTypesWithContext (context, argType, paramType);
12519
12582
}
12520
12583
}
12521
12584
@@ -12530,7 +12593,7 @@ namespace ts {
12530
12593
if (excludeArgument[i] === false) {
12531
12594
const arg = args[i];
12532
12595
const paramType = getTypeAtPosition(signature, i);
12533
- inferTypes (context, checkExpressionWithContextualType(arg, paramType, inferenceMapper), paramType);
12596
+ inferTypesWithContext (context, checkExpressionWithContextualType(arg, paramType, inferenceMapper), paramType);
12534
12597
}
12535
12598
}
12536
12599
}
@@ -13617,7 +13680,7 @@ namespace ts {
13617
13680
for (let i = 0; i < len; i++) {
13618
13681
const declaration = <ParameterDeclaration>signature.parameters[i].valueDeclaration;
13619
13682
if (declaration.type) {
13620
- inferTypes (mapper.context, getTypeFromTypeNode(declaration.type), getTypeAtPosition(context, i));
13683
+ inferTypesWithContext (mapper.context, getTypeFromTypeNode(declaration.type), getTypeAtPosition(context, i));
13621
13684
}
13622
13685
}
13623
13686
}
@@ -13703,7 +13766,7 @@ namespace ts {
13703
13766
// T in the second overload so that we do not infer Base as a candidate for T
13704
13767
// (inferring Base would make type argument inference inconsistent between the two
13705
13768
// overloads).
13706
- inferTypes (mapper.context, links.type, instantiateType(contextualType, mapper));
13769
+ inferTypesWithContext (mapper.context, links.type, instantiateType(contextualType, mapper));
13707
13770
}
13708
13771
}
13709
13772
0 commit comments