@@ -27911,23 +27911,44 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
27911
27911
// constituent types keyed by the literal types of the property by that name in each constituent type.
27912
27912
function getKeyPropertyName(unionType: UnionType): __String | undefined {
27913
27913
const types = unionType.types;
27914
- // We only construct maps for unions with many non-primitive constituents.
27915
27914
if (
27916
27915
types.length < 10 || getObjectFlags(unionType) & ObjectFlags.PrimitiveUnion ||
27917
27916
countWhere(types, t => !!(t.flags & (TypeFlags.Object | TypeFlags.InstantiableNonPrimitive))) < 10
27918
27917
) {
27919
27918
return undefined;
27920
27919
}
27921
27920
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;
27931
27952
}
27932
27953
return (unionType.keyPropertyName as string).length ? unionType.keyPropertyName : undefined;
27933
27954
}
0 commit comments