@@ -25405,23 +25405,32 @@ namespace ts {
25405
25405
if (!assumeTrue) {
25406
25406
return filterType(type, t => !isRelated(t, candidate));
25407
25407
}
25408
- // If the current type is a union type, remove all constituents that couldn't be instances of
25409
- // the candidate type. If one or more constituents remain, return a union of those.
25410
- if (type.flags & TypeFlags.Union) {
25411
- const assignableType = filterType(type, t => isRelated(t, candidate));
25412
- if (!(assignableType.flags & TypeFlags.Never)) {
25413
- return assignableType;
25414
- }
25415
- }
25416
- // If the candidate type is a subtype of the target type, narrow to the candidate type.
25417
- // Otherwise, if the target type is assignable to the candidate type, keep the target type.
25418
- // Otherwise, if the candidate type is assignable to the target type, narrow to the candidate
25419
- // type. Otherwise, the types are completely unrelated, so narrow to an intersection of the
25420
- // two types.
25421
- return isTypeSubtypeOf(candidate, type) ? candidate :
25422
- isTypeAssignableTo(type, candidate) ? type :
25423
- isTypeAssignableTo(candidate, type) ? candidate :
25424
- getIntersectionType([type, candidate]);
25408
+ if (type.flags & TypeFlags.AnyOrUnknown) {
25409
+ return candidate;
25410
+ }
25411
+ // We first attempt to filter the current type, narrowing constituents as appropriate and removing
25412
+ // constituents that are unrelated to the candidate.
25413
+ const keyPropertyName = type.flags & TypeFlags.Union ? getKeyPropertyName(type as UnionType) : undefined;
25414
+ const narrowedType = mapType(candidate, c => {
25415
+ // If a discriminant property is available, use that to reduce the type.
25416
+ const discriminant = keyPropertyName && getTypeOfPropertyOfType(c, keyPropertyName);
25417
+ const matching = discriminant && getConstituentTypeForKeyType(type as UnionType, discriminant);
25418
+ // For each constituent t in the current type, if t and and c are directly related, pick the most
25419
+ // specific of the two.
25420
+ const directlyRelated = mapType(matching || type, t => isRelated(t, c) ? t : isRelated(c, t) ? c : neverType);
25421
+ // If no constituents are directly related, create intersections for any generic constituents that
25422
+ // are related by constraint.
25423
+ return directlyRelated.flags & TypeFlags.Never ?
25424
+ mapType(type, t => maybeTypeOfKind(t, TypeFlags.Instantiable) && isRelated(c, getBaseConstraintOfType(t) || unknownType) ? getIntersectionType([t, c]) : neverType) :
25425
+ directlyRelated;
25426
+ });
25427
+ // If filtering produced a non-empty type, return that. Otherwise, pick the most specific of the two
25428
+ // based on assignability, or as a last resort produce an intersection.
25429
+ return !(narrowedType.flags & TypeFlags.Never) ? narrowedType :
25430
+ isTypeSubtypeOf(candidate, type) ? candidate :
25431
+ isTypeAssignableTo(type, candidate) ? type :
25432
+ isTypeAssignableTo(candidate, type) ? candidate :
25433
+ getIntersectionType([type, candidate]);
25425
25434
}
25426
25435
25427
25436
function narrowTypeByCallExpression(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type {
0 commit comments