@@ -4494,7 +4494,6 @@ namespace ts {
4494
4494
function resolveMappedTypeMembers(type: MappedType) {
4495
4495
const members: SymbolTable = createMap<Symbol>();
4496
4496
let stringIndexInfo: IndexInfo;
4497
- let numberIndexInfo: IndexInfo;
4498
4497
// Resolve upfront such that recursive references see an empty object type.
4499
4498
setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined);
4500
4499
// In { [P in K]: T }, we refer to P as the type parameter type, K as the constraint type,
@@ -4529,16 +4528,8 @@ namespace ts {
4529
4528
else if (t.flags & TypeFlags.String) {
4530
4529
stringIndexInfo = createIndexInfo(propType, isReadonly);
4531
4530
}
4532
- else if (t.flags & TypeFlags.Number) {
4533
- numberIndexInfo = createIndexInfo(propType, isReadonly);
4534
- }
4535
4531
});
4536
- // If we created both a string and a numeric string index signature, and if the two index
4537
- // signatures have identical types, discard the redundant numeric index signature.
4538
- if (stringIndexInfo && numberIndexInfo && isTypeIdenticalTo(stringIndexInfo.type, numberIndexInfo.type)) {
4539
- numberIndexInfo = undefined;
4540
- }
4541
- setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
4532
+ setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined);
4542
4533
}
4543
4534
4544
4535
function getTypeParameterFromMappedType(type: MappedType) {
@@ -8432,7 +8423,7 @@ namespace ts {
8432
8423
// results for union and intersection types for performance reasons.
8433
8424
function couldContainTypeParameters(type: Type): boolean {
8434
8425
const objectFlags = getObjectFlags(type);
8435
- return !!(type.flags & TypeFlags.TypeParameter ||
8426
+ return !!(type.flags & ( TypeFlags.TypeParameter | TypeFlags.IndexedAccess) ||
8436
8427
objectFlags & ObjectFlags.Reference && forEach((<TypeReference>type).typeArguments, couldContainTypeParameters) ||
8437
8428
objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) ||
8438
8429
objectFlags & ObjectFlags.Mapped ||
@@ -8450,8 +8441,57 @@ namespace ts {
8450
8441
return type === typeParameter || type.flags & TypeFlags.UnionOrIntersection && forEach((<UnionOrIntersectionType>type).types, t => isTypeParameterAtTopLevel(t, typeParameter));
8451
8442
}
8452
8443
8453
- function inferTypes(context: InferenceContext, originalSource: Type, originalTarget: Type) {
8454
- const typeParameters = context.signature.typeParameters;
8444
+ // Infer a suitable input type for an isomorphic mapped type { [P in keyof T]: X }. We construct
8445
+ // an object type with the same set of properties as the source type, where the type of each
8446
+ // property is computed by inferring from the source property type to X for a synthetic type
8447
+ // parameter T[P] (i.e. we treat the type T[P] as the type parameter we're inferring for).
8448
+ function inferTypeForIsomorphicMappedType(source: Type, target: MappedType): Type {
8449
+ if (!isMappableType(source)) {
8450
+ return source;
8451
+ }
8452
+ const typeParameter = getIndexedAccessType((<IndexType>getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target));
8453
+ const typeParameterArray = [typeParameter];
8454
+ const typeInferences = createTypeInferencesObject();
8455
+ const typeInferencesArray = [typeInferences];
8456
+ const templateType = getTemplateTypeFromMappedType(target);
8457
+ const properties = getPropertiesOfType(source);
8458
+ const members = createSymbolTable(properties);
8459
+ let hasInferredTypes = false;
8460
+ for (const prop of properties) {
8461
+ const inferredPropType = inferTargetType(getTypeOfSymbol(prop));
8462
+ if (inferredPropType) {
8463
+ const inferredProp = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | prop.flags & SymbolFlags.Optional, prop.name);
8464
+ inferredProp.declarations = prop.declarations;
8465
+ inferredProp.type = inferredPropType;
8466
+ inferredProp.isReadonly = isReadonlySymbol(prop);
8467
+ members[prop.name] = inferredProp;
8468
+ hasInferredTypes = true;
8469
+ }
8470
+ }
8471
+ let indexInfo = getIndexInfoOfType(source, IndexKind.String);
8472
+ if (indexInfo) {
8473
+ const inferredIndexType = inferTargetType(indexInfo.type);
8474
+ if (inferredIndexType) {
8475
+ indexInfo = createIndexInfo(inferredIndexType, indexInfo.isReadonly);
8476
+ hasInferredTypes = true;
8477
+ }
8478
+ }
8479
+ return hasInferredTypes ? createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined) : source;
8480
+
8481
+ function inferTargetType(sourceType: Type): Type {
8482
+ typeInferences.primary = undefined;
8483
+ typeInferences.secondary = undefined;
8484
+ inferTypes(typeParameterArray, typeInferencesArray, sourceType, templateType);
8485
+ const inferences = typeInferences.primary || typeInferences.secondary;
8486
+ return inferences && getUnionType(inferences, /*subtypeReduction*/ true);
8487
+ }
8488
+ }
8489
+
8490
+ function inferTypesWithContext(context: InferenceContext, originalSource: Type, originalTarget: Type) {
8491
+ inferTypes(context.signature.typeParameters, context.inferences, originalSource, originalTarget);
8492
+ }
8493
+
8494
+ function inferTypes(typeParameters: Type[], typeInferences: TypeInferences[], originalSource: Type, originalTarget: Type) {
8455
8495
let sourceStack: Type[];
8456
8496
let targetStack: Type[];
8457
8497
let depth = 0;
@@ -8519,7 +8559,7 @@ namespace ts {
8519
8559
target = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>target, matchingTypes);
8520
8560
}
8521
8561
}
8522
- if (target.flags & TypeFlags.TypeParameter) {
8562
+ if (target.flags & ( TypeFlags.TypeParameter | TypeFlags.IndexedAccess) ) {
8523
8563
// If target is a type parameter, make an inference, unless the source type contains
8524
8564
// the anyFunctionType (the wildcard type that's used to avoid contextually typing functions).
8525
8565
// Because the anyFunctionType is internal, it should not be exposed to the user by adding
@@ -8531,7 +8571,7 @@ namespace ts {
8531
8571
}
8532
8572
for (let i = 0; i < typeParameters.length; i++) {
8533
8573
if (target === typeParameters[i]) {
8534
- const inferences = context.inferences [i];
8574
+ const inferences = typeInferences [i];
8535
8575
if (!inferences.isFixed) {
8536
8576
// Any inferences that are made to a type parameter in a union type are inferior
8537
8577
// to inferences made to a flat (non-union) type. This is because if we infer to
@@ -8545,7 +8585,7 @@ namespace ts {
8545
8585
if (!contains(candidates, source)) {
8546
8586
candidates.push(source);
8547
8587
}
8548
- if (!isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
8588
+ if (target.flags & TypeFlags.TypeParameter && !isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
8549
8589
inferences.topLevel = false;
8550
8590
}
8551
8591
}
@@ -8622,12 +8662,19 @@ namespace ts {
8622
8662
function inferFromObjectTypes(source: Type, target: Type) {
8623
8663
if (getObjectFlags(target) & ObjectFlags.Mapped) {
8624
8664
const constraintType = getConstraintTypeFromMappedType(<MappedType>target);
8625
- if (getObjectFlags(source) & ObjectFlags.Mapped) {
8626
- inferFromTypes(getConstraintTypeFromMappedType(<MappedType>source), constraintType);
8627
- inferFromTypes(getTemplateTypeFromMappedType(<MappedType>source), getTemplateTypeFromMappedType(<MappedType>target));
8665
+ if (constraintType.flags & TypeFlags.Index) {
8666
+ // We're inferring from some source type S to an isomorphic mapped type { [P in keyof T]: X },
8667
+ // where T is a type parameter. Use inferTypeForIsomorphicMappedType to infer a suitable source
8668
+ // type and then infer from that type to T.
8669
+ const index = indexOf(typeParameters, (<IndexType>constraintType).type);
8670
+ if (index >= 0 && !typeInferences[index].isFixed) {
8671
+ inferFromTypes(inferTypeForIsomorphicMappedType(source, <MappedType>target), typeParameters[index]);
8672
+ }
8628
8673
return;
8629
8674
}
8630
8675
if (constraintType.flags & TypeFlags.TypeParameter) {
8676
+ // We're inferring from some source type S to a mapped type { [P in T]: X }, where T is a type
8677
+ // parameter. Infer from 'keyof S' to T and infer from a union of each property type in S to X.
8631
8678
inferFromTypes(getIndexType(source), constraintType);
8632
8679
inferFromTypes(getUnionType(map(getPropertiesOfType(source), getTypeOfSymbol)), getTemplateTypeFromMappedType(<MappedType>target));
8633
8680
return;
@@ -12469,7 +12516,7 @@ namespace ts {
12469
12516
const context = createInferenceContext(signature, /*inferUnionTypes*/ true);
12470
12517
forEachMatchingParameterType(contextualSignature, signature, (source, target) => {
12471
12518
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
12472
- inferTypes (context, instantiateType(source, contextualMapper), target);
12519
+ inferTypesWithContext (context, instantiateType(source, contextualMapper), target);
12473
12520
});
12474
12521
return getSignatureInstantiation(signature, getInferredTypes(context));
12475
12522
}
@@ -12504,7 +12551,7 @@ namespace ts {
12504
12551
if (thisType) {
12505
12552
const thisArgumentNode = getThisArgumentOfCall(node);
12506
12553
const thisArgumentType = thisArgumentNode ? checkExpression(thisArgumentNode) : voidType;
12507
- inferTypes (context, thisArgumentType, thisType);
12554
+ inferTypesWithContext (context, thisArgumentType, thisType);
12508
12555
}
12509
12556
12510
12557
// We perform two passes over the arguments. In the first pass we infer from all arguments, but use
@@ -12526,7 +12573,7 @@ namespace ts {
12526
12573
argType = checkExpressionWithContextualType(arg, paramType, mapper);
12527
12574
}
12528
12575
12529
- inferTypes (context, argType, paramType);
12576
+ inferTypesWithContext (context, argType, paramType);
12530
12577
}
12531
12578
}
12532
12579
@@ -12541,7 +12588,7 @@ namespace ts {
12541
12588
if (excludeArgument[i] === false) {
12542
12589
const arg = args[i];
12543
12590
const paramType = getTypeAtPosition(signature, i);
12544
- inferTypes (context, checkExpressionWithContextualType(arg, paramType, inferenceMapper), paramType);
12591
+ inferTypesWithContext (context, checkExpressionWithContextualType(arg, paramType, inferenceMapper), paramType);
12545
12592
}
12546
12593
}
12547
12594
}
@@ -13628,7 +13675,7 @@ namespace ts {
13628
13675
for (let i = 0; i < len; i++) {
13629
13676
const declaration = <ParameterDeclaration>signature.parameters[i].valueDeclaration;
13630
13677
if (declaration.type) {
13631
- inferTypes (mapper.context, getTypeFromTypeNode(declaration.type), getTypeAtPosition(context, i));
13678
+ inferTypesWithContext (mapper.context, getTypeFromTypeNode(declaration.type), getTypeAtPosition(context, i));
13632
13679
}
13633
13680
}
13634
13681
}
@@ -13714,7 +13761,7 @@ namespace ts {
13714
13761
// T in the second overload so that we do not infer Base as a candidate for T
13715
13762
// (inferring Base would make type argument inference inconsistent between the two
13716
13763
// overloads).
13717
- inferTypes (mapper.context, links.type, instantiateType(contextualType, mapper));
13764
+ inferTypesWithContext (mapper.context, links.type, instantiateType(contextualType, mapper));
13718
13765
}
13719
13766
}
13720
13767
0 commit comments