mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-31 18:10:56 -05:00
Improve narrowing logic for instanceof, type predicate functions, and assertion functions (#49625)
* Improve narrowing logic for instanceof, type predicates, and assertions * Accept new baselines * Add tests * Tweak algorithm * Accept new baselines * Optimize for discriminated unions
This commit is contained in:
@@ -25405,23 +25405,32 @@ namespace ts {
|
||||
if (!assumeTrue) {
|
||||
return filterType(type, t => !isRelated(t, candidate));
|
||||
}
|
||||
// If the current type is a union type, remove all constituents that couldn't be instances of
|
||||
// the candidate type. If one or more constituents remain, return a union of those.
|
||||
if (type.flags & TypeFlags.Union) {
|
||||
const assignableType = filterType(type, t => isRelated(t, candidate));
|
||||
if (!(assignableType.flags & TypeFlags.Never)) {
|
||||
return assignableType;
|
||||
}
|
||||
if (type.flags & TypeFlags.AnyOrUnknown) {
|
||||
return candidate;
|
||||
}
|
||||
// If the candidate type is a subtype of the target type, narrow to the candidate type.
|
||||
// Otherwise, if the target type is assignable to the candidate type, keep the target type.
|
||||
// Otherwise, if the candidate type is assignable to the target type, narrow to the candidate
|
||||
// type. Otherwise, the types are completely unrelated, so narrow to an intersection of the
|
||||
// two types.
|
||||
return isTypeSubtypeOf(candidate, type) ? candidate :
|
||||
isTypeAssignableTo(type, candidate) ? type :
|
||||
isTypeAssignableTo(candidate, type) ? candidate :
|
||||
getIntersectionType([type, candidate]);
|
||||
// We first attempt to filter the current type, narrowing constituents as appropriate and removing
|
||||
// constituents that are unrelated to the candidate.
|
||||
const keyPropertyName = type.flags & TypeFlags.Union ? getKeyPropertyName(type as UnionType) : undefined;
|
||||
const narrowedType = mapType(candidate, c => {
|
||||
// If a discriminant property is available, use that to reduce the type.
|
||||
const discriminant = keyPropertyName && getTypeOfPropertyOfType(c, keyPropertyName);
|
||||
const matching = discriminant && getConstituentTypeForKeyType(type as UnionType, discriminant);
|
||||
// For each constituent t in the current type, if t and and c are directly related, pick the most
|
||||
// specific of the two.
|
||||
const directlyRelated = mapType(matching || type, t => isRelated(t, c) ? t : isRelated(c, t) ? c : neverType);
|
||||
// If no constituents are directly related, create intersections for any generic constituents that
|
||||
// are related by constraint.
|
||||
return directlyRelated.flags & TypeFlags.Never ?
|
||||
mapType(type, t => maybeTypeOfKind(t, TypeFlags.Instantiable) && isRelated(c, getBaseConstraintOfType(t) || unknownType) ? getIntersectionType([t, c]) : neverType) :
|
||||
directlyRelated;
|
||||
});
|
||||
// If filtering produced a non-empty type, return that. Otherwise, pick the most specific of the two
|
||||
// based on assignability, or as a last resort produce an intersection.
|
||||
return !(narrowedType.flags & TypeFlags.Never) ? narrowedType :
|
||||
isTypeSubtypeOf(candidate, type) ? candidate :
|
||||
isTypeAssignableTo(type, candidate) ? type :
|
||||
isTypeAssignableTo(candidate, type) ? candidate :
|
||||
getIntersectionType([type, candidate]);
|
||||
}
|
||||
|
||||
function narrowTypeByCallExpression(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type {
|
||||
|
||||
Reference in New Issue
Block a user