@@ -27911,23 +27911,44 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2791127911 // constituent types keyed by the literal types of the property by that name in each constituent type.
2791227912 function getKeyPropertyName(unionType: UnionType): __String | undefined {
2791327913 const types = unionType.types;
27914- // We only construct maps for unions with many non-primitive constituents.
2791527914 if (
2791627915 types.length < 10 || getObjectFlags(unionType) & ObjectFlags.PrimitiveUnion ||
2791727916 countWhere(types, t => !!(t.flags & (TypeFlags.Object | TypeFlags.InstantiableNonPrimitive))) < 10
2791827917 ) {
2791927918 return undefined;
2792027919 }
2792127920 if (unionType.keyPropertyName === undefined) {
27922- // The candidate key property name is the name of the first property with a unit type in one of the
27923- // constituent types.
27924- const keyPropertyName = forEach(types, t =>
27925- t.flags & (TypeFlags.Object | TypeFlags.InstantiableNonPrimitive) ?
27926- forEach(getPropertiesOfType(t), p => isUnitType(getTypeOfSymbol(p)) ? p.escapedName : undefined) :
27927- undefined);
27928- const mapByKeyProperty = keyPropertyName && mapTypesByKeyProperty(types, keyPropertyName);
27929- unionType.keyPropertyName = mapByKeyProperty ? keyPropertyName : "" as __String;
27930- unionType.constituentMap = mapByKeyProperty;
27921+ // Map property name to count of object types where it's a unit (literal) type
27922+ const propertyCounts: Map<string, number> = new Map();
27923+ let objectTypeCount = 0;
27924+
27925+ for (const t of types) {
27926+ if (t.flags & (TypeFlags.Object | TypeFlags.InstantiableNonPrimitive)) {
27927+ objectTypeCount++;
27928+ for (const p of getPropertiesOfType(t)) {
27929+ if (isUnitType(getTypeOfSymbol(p))) {
27930+ const name = p.escapedName as string;
27931+ propertyCounts.set(name, (propertyCounts.get(name) || 0) + 1);
27932+ }
27933+ }
27934+ }
27935+ }
27936+
27937+ // Choose property present with unit type in ALL object members, or the most common
27938+ let bestPropertyName: string | undefined;
27939+ let bestCount = 0;
27940+
27941+ for (const [name, count] of propertyCounts.entries()) {
27942+ // Prefer property present in all object types, otherwise pick the most frequent
27943+ if ((count > bestCount) || (count === objectTypeCount && count >= bestCount)) {
27944+ bestPropertyName = name;
27945+ bestCount = count;
27946+ }
27947+ }
27948+
27949+ const mapByKeyProperty = bestPropertyName && mapTypesByKeyProperty(types, bestPropertyName as __String);
27950+ unionType.keyPropertyName = mapByKeyProperty ? bestPropertyName as __String : "" as __String;
27951+ unionType.constituentMap = typeof mapByKeyProperty === "object" ? mapByKeyProperty : undefined;
2793127952 }
2793227953 return (unionType.keyPropertyName as string).length ? unionType.keyPropertyName : undefined;
2793327954 }
0 commit comments