@@ -25460,25 +25460,41 @@ namespace ts {
25460
25460
// control flow analysis an opportunity to narrow it further. For example, for a reference of a type
25461
25461
// parameter type 'T extends string | undefined' with a contextual type 'string', we substitute
25462
25462
// 'string | undefined' to give control flow analysis the opportunity to narrow to type 'string'.
25463
- if (checkMode && checkMode & CheckMode.Inferential) {
25463
+ if (checkMode && ( checkMode & CheckMode.Inferential) ) {
25464
25464
return type;
25465
25465
}
25466
25466
25467
- let contextualType: Type | undefined;
25468
- // If we aren't in a constraint position, or we can't find a contextual type, or the contextual type indicates
25469
- // that the type in question may be a direct inference source, then don't do anything special.
25470
- if (!isConstraintPosition(type, reference) && !(contextualType = tryGetContextualTypeWithNoGenericTypes(reference, checkMode))) {
25467
+ // Bail-out early if we don't have anything instantiable in the first place.
25468
+ if (!someType(type, t => !!(t.flags & TypeFlags.Instantiable))) {
25471
25469
return type;
25472
25470
}
25473
25471
25472
+ const hasGenericWithUnionConstraint = someType(type, isGenericTypeWithUnionConstraint);
25473
+ // If we only care about the apparent types of a value's constraints, we should narrow based on the constraint.
25474
+ if (hasGenericWithUnionConstraint && isConstraintPosition(type, reference)) {
25475
+ return getTypeWithConstraintsSubstituted();
25476
+ }
25477
+
25478
+ const contextualType = tryGetContextualTypeWithNoGenericTypes(reference, checkMode);
25479
+ // If there's no contextual type, that's a signal that we don't need to perform any substitution.
25480
+ // If there *is* a contextual type, but it has top-level type variables, then it's not appropriate to narrow on
25481
+ // constraints since the original type may be inferred from.
25482
+ if (!contextualType) {
25483
+ return type;
25484
+ }
25485
+
25486
+ // When we have a type parameter constrained to a union type, or unknown, we can typically narrow on the constraint to get better results.
25474
25487
const substituteConstraints =
25475
- // When we have a type parameter constrained to a union type, we can typically narrow to get better results.
25476
- someType(type, isGenericTypeWithUnionConstraint) ||
25488
+ hasGenericWithUnionConstraint ||
25477
25489
// When the contextual type is 'unknown', we may need to narrow for compatibility with non-null targets.
25478
25490
// This allows some parity with a constraint of '{} | null | undefined'.
25479
- (type.flags & TypeFlags.Instantiable) && contextualType && isEmptyObjectType(contextualType);
25491
+ (getBaseConstraintOfType(type) || unknownType) === unknownType && isEmptyObjectType(contextualType);
25492
+
25493
+ return substituteConstraints ? getTypeWithConstraintsSubstituted() : type;
25480
25494
25481
- return substituteConstraints ? mapType(type, t => t.flags & TypeFlags.Instantiable ? getBaseConstraintOfType(t) || unknownType : t) : type;
25495
+ function getTypeWithConstraintsSubstituted() {
25496
+ return mapType(type, t => t.flags & TypeFlags.Instantiable ? getBaseConstraintOfType(t) || unknownType : t);
25497
+ }
25482
25498
}
25483
25499
25484
25500
function isExportOrExportExpression(location: Node) {
0 commit comments