Skip to content

Generic inference different between equivalent function expression and shorthand method in object literal #32230

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

Closed
minajevs opened this issue Jul 3, 2019 · 5 comments · Fixed by #32362
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@minajevs
Copy link
Contributor

minajevs commented Jul 3, 2019

TypeScript Version: 3.5.1

Search Terms: parameter, inference, order, generic, function expression, function declaration

Code

type Callback<TFoo, TBar> = (foo: TFoo, bar: TBar) => any

declare function example<TFoo, TBar, TCallback extends Callback<TFoo, TBar>>(
    foo: TFoo,
    callback: TCallback,
    bar: TBar,
): TCallback

// bar is infered to be 'string'
example(42, (foo, bar) => ({
    t: () => { }
}), '42')

// bar isn't infered and is 'unknown'
example(42, (foo, bar) => ({
    t() { }
}), '42')

Expected behavior:
foo and bar should be inferred as number and string respectively in both cases

Actual behavior:
When not using arrow function expression bar is not inferred, even though foo is infered in both cases

Playground Link: click

Related Issues: This code worked in TS 3.3.3 , so #31814 is most definitely related, and so probably #30215 is the root cause. #6627 is somewhat related.

Comment: Type inference stops working for types which appear in arguments after inner call to a generic function when function declaration appears in callback return. Using function expressions instead is limiting because those does not have correct this context.

Is this behavior caused by the same limitations as #31814, or is it something what can be improved?

@RyanCavanaugh
Copy link
Member

Nit: These are shorthand method definitions, not function declarations.

foo and bar should be inferred as string in both cases

This doesn't seem correct. example could be

function example<TFoo, TBar, TCallback extends Callback<TFoo, TBar>>(
    foo: TFoo,
    callback: TCallback,
    bar: TBar,
): TCallback {
    callback(foo, bar);
    return callback;
}

In which case the first call will definitely observe foo === 42.

@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Jul 3, 2019
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 3.6.0 milestone Jul 3, 2019
@RyanCavanaugh
Copy link
Member

@ahejlsberg I might be missing something but I don't see any reason for the two to behave differently

@RyanCavanaugh RyanCavanaugh changed the title Generic type inference fails when using function declaration, and does not when using function expression Generic inference different between equivalent function expression and shorthand method in object literal Jul 3, 2019
@minajevs
Copy link
Contributor Author

minajevs commented Jul 4, 2019

Nit: These are shorthand method definitions, not function declarations

Thanks, I had a hard time finding correct name for that.

This doesn't seem correct.

Oh yeah, I described it poorly. Changed it to "foo and bar should be inferred as number and string respectively in both cases"

@minajevs
Copy link
Contributor Author

minajevs commented Jul 11, 2019

I am trying to investigate it myself. This bug appears after this commit. Why exactly non-fixing mapper causes that I am yet to find.

@ahejlsberg
Copy link
Member

This issue is somewhat involved. The core issue is cache invalidation during type inference. Specifically, when we make new inferences for a particular type parameter we only invalidate the cache for that type parameter. In the example above, TCallback indirectly depends on inferences for TFoo and TBar because, when we have no candidates for TCallback, we default to the constraint Callback<TFoo, TBar>. This may cause us to cache an inference for TCallback based on the current inferences for TFoo and TBar and then not recompute when new inferences are made for TFoo and TBar.

I think the fix is to invalidate all caches for non-fixed type parameters when inferences are made for a particular type parameter in an inference context. I will look into it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants