Skip to content

Commit 21fde45

Browse files
Try to change some logic for contextual narrowing for any instantiable type to empty objects.
1 parent d4e363a commit 21fde45

File tree

1 file changed

+21
-6
lines changed

1 file changed

+21
-6
lines changed

src/compiler/checker.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25439,7 +25439,7 @@ namespace ts {
2543925439
return !!(type.flags & TypeFlags.Instantiable && !maybeTypeOfKind(getBaseConstraintOrType(type), TypeFlags.Nullable));
2544025440
}
2544125441

25442-
function hasContextualTypeWithNoGenericTypes(node: Node, checkMode: CheckMode | undefined) {
25442+
function tryGetContextualTypeWithNoGenericTypes(node: Node, checkMode: CheckMode | undefined) {
2544325443
// Computing the contextual type for a child of a JSX element involves resolving the type of the
2544425444
// element's tag name, so we exclude that here to avoid circularities.
2544525445
// If check mode has `CheckMode.RestBindingElement`, we skip binding pattern contextual types,
@@ -25449,7 +25449,7 @@ namespace ts {
2544925449
(checkMode && checkMode & CheckMode.RestBindingElement ?
2545025450
getContextualType(node, ContextFlags.SkipBindingPatterns)
2545125451
: getContextualType(node));
25452-
return contextualType && !isGenericType(contextualType);
25452+
return contextualType && !isGenericType(contextualType) ? contextualType : undefined;
2545325453
}
2545425454

2545525455
function getNarrowableTypeForReference(type: Type, reference: Node, checkMode?: CheckMode) {
@@ -25460,10 +25460,25 @@ namespace ts {
2546025460
// control flow analysis an opportunity to narrow it further. For example, for a reference of a type
2546125461
// parameter type 'T extends string | undefined' with a contextual type 'string', we substitute
2546225462
// '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;
2546725482
}
2546825483

2546925484
function isExportOrExportExpression(location: Node) {

0 commit comments

Comments
 (0)