@@ -25439,7 +25439,7 @@ namespace ts {
25439
25439
return !!(type.flags & TypeFlags.Instantiable && !maybeTypeOfKind(getBaseConstraintOrType(type), TypeFlags.Nullable));
25440
25440
}
25441
25441
25442
- function hasContextualTypeWithNoGenericTypes (node: Node, checkMode: CheckMode | undefined) {
25442
+ function tryGetContextualTypeWithNoGenericTypes (node: Node, checkMode: CheckMode | undefined) {
25443
25443
// Computing the contextual type for a child of a JSX element involves resolving the type of the
25444
25444
// element's tag name, so we exclude that here to avoid circularities.
25445
25445
// If check mode has `CheckMode.RestBindingElement`, we skip binding pattern contextual types,
@@ -25449,7 +25449,7 @@ namespace ts {
25449
25449
(checkMode && checkMode & CheckMode.RestBindingElement ?
25450
25450
getContextualType(node, ContextFlags.SkipBindingPatterns)
25451
25451
: getContextualType(node));
25452
- return contextualType && !isGenericType(contextualType);
25452
+ return contextualType && !isGenericType(contextualType) ? contextualType : undefined ;
25453
25453
}
25454
25454
25455
25455
function getNarrowableTypeForReference(type: Type, reference: Node, checkMode?: CheckMode) {
@@ -25460,10 +25460,25 @@ 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
- const substituteConstraints = !(checkMode && checkMode & CheckMode.Inferential) &&
25464
- someType(type, isGenericTypeWithUnionConstraint) &&
25465
- (isConstraintPosition(type, reference) || hasContextualTypeWithNoGenericTypes(reference, checkMode));
25466
- return substituteConstraints ? mapType(type, t => t.flags & TypeFlags.Instantiable ? getBaseConstraintOrType(t) : t) : type;
25463
+ if (checkMode && checkMode & CheckMode.Inferential) {
25464
+ return type;
25465
+ }
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))) {
25471
+ return type;
25472
+ }
25473
+
25474
+ 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) ||
25477
+ // When the contextual type is 'unknown', we may need to narrow for compatibility with non-null targets.
25478
+ // This allows some parity with a constraint of '{} | null | undefined'.
25479
+ (type.flags & TypeFlags.Instantiable) && contextualType && isEmptyObjectType(contextualType);
25480
+
25481
+ return substituteConstraints ? mapType(type, t => t.flags & TypeFlags.Instantiable ? getBaseConstraintOfType(t) || unknownType : t) : type;
25467
25482
}
25468
25483
25469
25484
function isExportOrExportExpression(location: Node) {
0 commit comments