diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8b1c9c3eae2..118af6486ed 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11963,29 +11963,31 @@ namespace ts { } function findMostOverlappyType(source: Type, unionTarget: UnionOrIntersectionType) { - if (!(source.flags & (TypeFlags.Object | TypeFlags.Intersection))) { - return undefined; - } - const sourceProperties = getPropertiesOfType(source); - let bestType; - let count = -1; + let bestMatch: Type | undefined; + let matchingCount = 0; for (const target of unionTarget.types) { - if (!(target.flags & (TypeFlags.Object | TypeFlags.Intersection))) { - continue; + const overlap = getIntersectionType([getIndexType(source), getIndexType(target)]); + if (overlap.flags & TypeFlags.Index) { + // perfect overlap of keys + bestMatch = target; + matchingCount = Infinity; } - - let currentCount = 0; - for (const prop of sourceProperties) { - if (getPropertyOfType(target, prop.escapedName)) { - currentCount++; + else if (overlap.flags & TypeFlags.Union) { + // Some subset overlap if we have only string literals. + // If we have a union of index types, it seems likely that we + // needed to elaborate between two generic mapped types anyway. + const len = length((overlap as UnionType).types); + if (len >= matchingCount) { + bestMatch = target; + matchingCount = len; } } - if (currentCount >= count) { - count = currentCount; - bestType = target; + else if (!(overlap.flags & TypeFlags.Never) && 1 >= matchingCount) { + bestMatch = target; + matchingCount = 1; } } - return bestType; + return bestMatch; } // Keep this up-to-date with the same logic within `getApparentTypeOfContextualType`, since they should behave similarly