@@ -4494,7 +4494,6 @@ namespace ts {
44944494 function resolveMappedTypeMembers(type: MappedType) {
44954495 const members: SymbolTable = createMap<Symbol>();
44964496 let stringIndexInfo: IndexInfo;
4497- let numberIndexInfo: IndexInfo;
44984497 // Resolve upfront such that recursive references see an empty object type.
44994498 setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined);
45004499 // 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 {
45294528 else if (t.flags & TypeFlags.String) {
45304529 stringIndexInfo = createIndexInfo(propType, isReadonly);
45314530 }
4532- else if (t.flags & TypeFlags.Number) {
4533- numberIndexInfo = createIndexInfo(propType, isReadonly);
4534- }
45354531 });
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);
45424533 }
45434534
45444535 function getTypeParameterFromMappedType(type: MappedType) {
@@ -8432,7 +8423,7 @@ namespace ts {
84328423 // results for union and intersection types for performance reasons.
84338424 function couldContainTypeParameters(type: Type): boolean {
84348425 const objectFlags = getObjectFlags(type);
8435- return !!(type.flags & TypeFlags.TypeParameter ||
8426+ return !!(type.flags & ( TypeFlags.TypeParameter | TypeFlags.IndexedAccess) ||
84368427 objectFlags & ObjectFlags.Reference && forEach((<TypeReference>type).typeArguments, couldContainTypeParameters) ||
84378428 objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) ||
84388429 objectFlags & ObjectFlags.Mapped ||
@@ -8450,8 +8441,57 @@ namespace ts {
84508441 return type === typeParameter || type.flags & TypeFlags.UnionOrIntersection && forEach((<UnionOrIntersectionType>type).types, t => isTypeParameterAtTopLevel(t, typeParameter));
84518442 }
84528443
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) {
84558495 let sourceStack: Type[];
84568496 let targetStack: Type[];
84578497 let depth = 0;
@@ -8519,7 +8559,7 @@ namespace ts {
85198559 target = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>target, matchingTypes);
85208560 }
85218561 }
8522- if (target.flags & TypeFlags.TypeParameter) {
8562+ if (target.flags & ( TypeFlags.TypeParameter | TypeFlags.IndexedAccess) ) {
85238563 // If target is a type parameter, make an inference, unless the source type contains
85248564 // the anyFunctionType (the wildcard type that's used to avoid contextually typing functions).
85258565 // Because the anyFunctionType is internal, it should not be exposed to the user by adding
@@ -8531,7 +8571,7 @@ namespace ts {
85318571 }
85328572 for (let i = 0; i < typeParameters.length; i++) {
85338573 if (target === typeParameters[i]) {
8534- const inferences = context.inferences [i];
8574+ const inferences = typeInferences [i];
85358575 if (!inferences.isFixed) {
85368576 // Any inferences that are made to a type parameter in a union type are inferior
85378577 // to inferences made to a flat (non-union) type. This is because if we infer to
@@ -8545,7 +8585,7 @@ namespace ts {
85458585 if (!contains(candidates, source)) {
85468586 candidates.push(source);
85478587 }
8548- if (!isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
8588+ if (target.flags & TypeFlags.TypeParameter && !isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
85498589 inferences.topLevel = false;
85508590 }
85518591 }
@@ -8622,12 +8662,19 @@ namespace ts {
86228662 function inferFromObjectTypes(source: Type, target: Type) {
86238663 if (getObjectFlags(target) & ObjectFlags.Mapped) {
86248664 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+ }
86288673 return;
86298674 }
86308675 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.
86318678 inferFromTypes(getIndexType(source), constraintType);
86328679 inferFromTypes(getUnionType(map(getPropertiesOfType(source), getTypeOfSymbol)), getTemplateTypeFromMappedType(<MappedType>target));
86338680 return;
@@ -12469,7 +12516,7 @@ namespace ts {
1246912516 const context = createInferenceContext(signature, /*inferUnionTypes*/ true);
1247012517 forEachMatchingParameterType(contextualSignature, signature, (source, target) => {
1247112518 // 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);
1247312520 });
1247412521 return getSignatureInstantiation(signature, getInferredTypes(context));
1247512522 }
@@ -12504,7 +12551,7 @@ namespace ts {
1250412551 if (thisType) {
1250512552 const thisArgumentNode = getThisArgumentOfCall(node);
1250612553 const thisArgumentType = thisArgumentNode ? checkExpression(thisArgumentNode) : voidType;
12507- inferTypes (context, thisArgumentType, thisType);
12554+ inferTypesWithContext (context, thisArgumentType, thisType);
1250812555 }
1250912556
1251012557 // We perform two passes over the arguments. In the first pass we infer from all arguments, but use
@@ -12526,7 +12573,7 @@ namespace ts {
1252612573 argType = checkExpressionWithContextualType(arg, paramType, mapper);
1252712574 }
1252812575
12529- inferTypes (context, argType, paramType);
12576+ inferTypesWithContext (context, argType, paramType);
1253012577 }
1253112578 }
1253212579
@@ -12541,7 +12588,7 @@ namespace ts {
1254112588 if (excludeArgument[i] === false) {
1254212589 const arg = args[i];
1254312590 const paramType = getTypeAtPosition(signature, i);
12544- inferTypes (context, checkExpressionWithContextualType(arg, paramType, inferenceMapper), paramType);
12591+ inferTypesWithContext (context, checkExpressionWithContextualType(arg, paramType, inferenceMapper), paramType);
1254512592 }
1254612593 }
1254712594 }
@@ -13628,7 +13675,7 @@ namespace ts {
1362813675 for (let i = 0; i < len; i++) {
1362913676 const declaration = <ParameterDeclaration>signature.parameters[i].valueDeclaration;
1363013677 if (declaration.type) {
13631- inferTypes (mapper.context, getTypeFromTypeNode(declaration.type), getTypeAtPosition(context, i));
13678+ inferTypesWithContext (mapper.context, getTypeFromTypeNode(declaration.type), getTypeAtPosition(context, i));
1363213679 }
1363313680 }
1363413681 }
@@ -13714,7 +13761,7 @@ namespace ts {
1371413761 // T in the second overload so that we do not infer Base as a candidate for T
1371513762 // (inferring Base would make type argument inference inconsistent between the two
1371613763 // overloads).
13717- inferTypes (mapper.context, links.type, instantiateType(contextualType, mapper));
13764+ inferTypesWithContext (mapper.context, links.type, instantiateType(contextualType, mapper));
1371813765 }
1371913766 }
1372013767
0 commit comments