Skip to content

Commit 17b7226

Browse files
Change in logic, up-front check for instantiables.
1 parent df3766d commit 17b7226

File tree

1 file changed

+25
-9
lines changed

1 file changed

+25
-9
lines changed

src/compiler/checker.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25460,25 +25460,41 @@ 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-
if (checkMode && checkMode & CheckMode.Inferential) {
25463+
if (checkMode && (checkMode & CheckMode.Inferential)) {
2546425464
return type;
2546525465
}
2546625466

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))) {
2547125469
return type;
2547225470
}
2547325471

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.
2547425487
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 ||
2547725489
// When the contextual type is 'unknown', we may need to narrow for compatibility with non-null targets.
2547825490
// 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;
2548025494

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+
}
2548225498
}
2548325499

2548425500
function isExportOrExportExpression(location: Node) {

0 commit comments

Comments
 (0)