diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 23c5568f2e5..ce35ddd522d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7084,6 +7084,39 @@ namespace ts { setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined); } + // Return the lower bound of the key type in a mapped type. Intuitively, the lower + // bound includes those keys that are known to always be present, for example because + // because of constraints on type parameters (e.g. 'keyof T' for a constrained T). + function getLowerBoundOfKeyType(type: Type): Type { + if (type.flags & (TypeFlags.Any | TypeFlags.Primitive)) { + return type; + } + if (type.flags & TypeFlags.Index) { + return getIndexType(getApparentType((type).type)); + } + if (type.flags & TypeFlags.Conditional) { + return getLowerBoundOfConditionalType(type); + } + if (type.flags & TypeFlags.Union) { + return getUnionType(sameMap((type).types, getLowerBoundOfKeyType)); + } + if (type.flags & TypeFlags.Intersection) { + return getIntersectionType(sameMap((type).types, getLowerBoundOfKeyType)); + } + return neverType; + } + + function getLowerBoundOfConditionalType(type: ConditionalType) { + if (type.root.isDistributive) { + const constraint = getLowerBoundOfKeyType(type.checkType); + if (constraint !== type.checkType) { + const mapper = makeUnaryTypeMapper(type.root.checkType, constraint); + return getConditionalTypeInstantiation(type, combineTypeMappers(mapper, type.mapper)); + } + } + return type; + } + /** Resolve the members of a mapped type { [P in K]: T } */ function resolveMappedTypeMembers(type: MappedType) { const members: SymbolTable = createSymbolTable(); @@ -7112,10 +7145,7 @@ namespace ts { } } else { - // If the key type is a 'keyof X', obtain 'keyof C' where C is the base constraint of X. - // Then iterate over the constituents of the key type. - const iterationType = constraintType.flags & TypeFlags.Index ? getIndexType(getApparentType((constraintType).type)) : constraintType; - forEachType(iterationType, addMemberForKeyType); + forEachType(getLowerBoundOfKeyType(constraintType), addMemberForKeyType); } setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);