Skip to content

Commit 3e2914f

Browse files
committed
Fix: update checker.ts for order-independent discriminant property selection
1 parent c380780 commit 3e2914f

File tree

1 file changed

+31
-10
lines changed

1 file changed

+31
-10
lines changed

src/compiler/checker.ts

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)