Skip to content
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

Improve generic function inference #30193

Merged
merged 7 commits into from
Mar 8, 2019
Merged

Conversation

ahejlsberg
Copy link
Member

This PR addresses several issues related to type inference involving combinations of contextually typed arrow functions and generic functions.

In general, type argument inference defers processing of contextually typed arrow functions and function expressions as long as possible such that inferences can be collected from other arguments before those inferences are fixed and used to type the arrow function or function expression parameters. For example:

declare function invoke<T, U>(f: (x: T, y: T) => U, x: T, y: T): U;
let thirty = invoke((x, y) => x + y, 10, 20);

Here, we defer processing of the arrow function until we have made inferences the two number arguments and then use those inferences to assign type number to x and y.

Previously, contextually typed arrow functions and function expressions were the only types of arguments for which we'd defer inference. However, arguments with generic function types are also be subject to contextual typing. Consider:

declare function pipe<A extends any[], B, C>(ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C;
declare function identity<T>(x: T): T;
declare function list<T>(x: T): T[];
declare function box<V>(x: V): { value: V };

const f1: <T>(x: T) => { value: T[] } = pipe(list, box);  // Ok
const f2: <T>(x: T) => { value: T[] } = pipe(x => [x], box);  // Was error, now ok
const f3: <T>(x: T) => { value: T[][] } = pipe(x => [x], pipe(x => [x], box));  // Was error, now ok

Before this PR we would error on f2 because we'd process the box argument ahead of the x => [x] argument and miss the opportunity to have inferences flow from the return type annotation into A and from the result of x => [x] into B before using inferences for B to contextually type box. Likewise, we'd error on f3 because we'd process the inner pipe call before the x => [x] arrow function.

With this PR we now defer processing of arguments having generic function types along with contextually typed arrow functions and function expressions.

Fixes #25791.
Fixes #25826.

@@ -22060,7 +22089,7 @@ namespace ts {
forEachReturnStatement(<Block>func.body, returnStatement => {
const expr = returnStatement.expression;
if (expr) {
let type = checkExpressionCached(expr, checkMode);
let type = checkExpressionCached(expr, checkMode && checkMode && checkMode & ~CheckMode.SkipGenericFunctions);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line seems like a typo.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just wanted to be extra sure!

Copy link
Member

@DanielRosenwasser DanielRosenwasser Mar 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I heard this is how you make really really thread-safe code in Node dot jay ess.

@ahejlsberg ahejlsberg merged commit e7881a4 into master Mar 8, 2019
@ahejlsberg ahejlsberg deleted the deferGenericFunctionInference branch May 24, 2019 13:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants