Various fixes for circularity issue

This commit is contained in:
Dan Vanderkam
2024-02-23 10:51:44 -05:00
parent a72b1f1743
commit ef2d465047

View File

@@ -15466,7 +15466,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
else if (signature.declaration && isFunctionLikeDeclaration(signature.declaration) && (!signature.resolvedReturnType || signature.resolvedReturnType === booleanType)) {
const { declaration } = signature;
signature.resolvedTypePredicate = noTypePredicate; // avoid infinite loop
signature.resolvedTypePredicate = getTypePredicateFromBody(declaration, signature) || noTypePredicate;
signature.resolvedTypePredicate = getTypePredicateFromBody(declaration) || noTypePredicate;
}
else {
signature.resolvedTypePredicate = noTypePredicate;
@@ -37399,9 +37399,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
function getTypePredicateFromBody(func: FunctionLikeDeclaration, _sig: Signature): TypePredicate | undefined {
function getTypePredicateFromBody(func: FunctionLikeDeclaration): TypePredicate | undefined {
switch (func.kind) {
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return undefined;
}
const functionFlags = getFunctionFlags(func);
if (functionFlags !== FunctionFlags.Normal) return undefined;
if (functionFlags !== FunctionFlags.Normal || func.parameters.length === 0) return undefined;
// Only attempt to infer a type predicate if there's exactly one return.
let singleReturn: Expression | undefined;
@@ -37418,6 +37424,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (bailedEarly || !singleReturn) return undefined;
}
if (isTriviallyNonBoolean(singleReturn)) return undefined;
const predicate = checkIfExpressionRefinesAnyParameter(singleReturn);
if (predicate) {
const [i, type] = predicate;
@@ -37431,7 +37439,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function checkIfExpressionRefinesAnyParameter(expr: Expression): [number, Type] | undefined {
expr = skipParentheses(expr, /*excludeJSDocTypeAssertions*/ true);
const type = checkExpressionCached(expr, CheckMode.TypeOnly);
const type = checkExpressionCached(expr); // , CheckMode.TypeOnly);
if (type !== booleanType || !func.body) return undefined;
return forEach(func.parameters, (param, i) => {
@@ -37477,6 +37485,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return trueType;
}
}
// This bypasses the call to checkExpression for expressions that are clearly not booleans.
// In addition to potentially saving work, this avoids some circularlity issues.
function isTriviallyNonBoolean(expr: Expression): boolean {
if (isLiteralExpression(expr) || isLiteralExpressionOfObject(expr)) {
return true;
}
if (isIdentifier(expr)) {
const sym = getResolvedSymbol(expr);
if (sym.flags & (SymbolFlags.Class | SymbolFlags.ObjectLiteral | SymbolFlags.Function | SymbolFlags.Enum | SymbolFlags.EnumMember)) {
return true;
}
}
return false; // may or may not be boolean
}
}
/**