-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Infer from context sensitive return expressions #60909
base: main
Are you sure you want to change the base?
Infer from context sensitive return expressions #60909
Conversation
inferFromAnnotatedParameters(signature, contextualSignature, inferenceContext!); | ||
} | ||
} | ||
if (contextualSignature && !getReturnTypeFromAnnotation(node) && !signature.resolvedReturnType) { | ||
const returnType = getReturnTypeFromBody(node, checkMode); | ||
let contextualReturnType: Type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I literally wrote this code in August so I need to take another look at this, self-review it and add more tests. In the meantime though, it could be helpful for me to learn what the extended test suite thinks about this. cc @jakebailey :p
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, damn self-check :p I need to fix this first: repro
@typescript-bot test it |
Hey @jakebailey, the results of running the DT tests are ready. Everything looks the same! |
@jakebailey Here are the results of running the user tests with tsc comparing Everything looks good! |
@jakebailey Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
@jakebailey Here are the results of running the top 400 repos with tsc comparing Everything looks good! |
Ok, I think this one is in good shape for review. I've only made a small change to the code - I don't think it will impact any code in the wild but it might be good to rerun the extended tests. |
@@ -1329,11 +1329,14 @@ export const enum CheckMode { | |||
Inferential = 1 << 1, // Inferential typing | |||
SkipContextSensitive = 1 << 2, // Skip context sensitive function expressions | |||
SkipGenericFunctions = 1 << 3, // Skip single signature generic functions | |||
IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help | |||
RestBindingElement = 1 << 5, // Checking a type that is going to be used to determine the type of a rest binding element | |||
SkipReturnTypeFromBodyInference = 1 << 4, // Skip inferring from return types of context sensitive functions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's separate from SkipGenericFunctions
because getReturnTypeFromBody
removes SkipGenericFunctions
bit - so when the compiler gets to a function node with type parameters within a context-sensitive function's return it can't just use SkipGenericFunctions
to skip over inferring its own return type from body.
The goal of SkipReturnTypeFromBodyInference
is to persist through getReturnTypeFromBody
.
const inferenceContext = getInferenceContext(node); | ||
const isReturnContextSensitive = !!node.body && (node.body.kind === SyntaxKind.Block ? forEachReturnStatement(node.body as Block, statement => !!statement.expression && isContextSensitive(statement.expression)) : isContextSensitive(node.body)); | ||
returnType = getReturnTypeFromBody(node, checkMode | (isReturnContextSensitive ? CheckMode.SkipContextSensitive : 0)); | ||
inferTypes(inferenceContext!.inferences, returnType, contextualReturnType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
alternatively, this could be handled by intra expression inference but that is order-sensitive and traditionally simple cases like this one aren't:
declare function test<T>(_: {
stuff: T,
consume: (arg: T) => void
}): void
test({
consume: (arg) => {},
stuff: 'foo' // this can come after `consume`
})
So the reason I put this logic here is that it allows for the same order-independence
fixes #60720
fixes #57021