Skip to content

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

Closed

Description

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?

Activity

  1. RyanCavanaugh commented on Jul 3, 2019

    @RyanCavanaugh
    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.

  2. RyanCavanaugh commented on Jul 3, 2019

    @RyanCavanaugh
    Member

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

  3. 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[/+] on Jul 3, 2019
  4. minajevs commented on Jul 4, 2019

    @minajevs
    ContributorAuthor

    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"

  5. minajevs commented on Jul 11, 2019

    @minajevs
    ContributorAuthor

    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.

  6. ahejlsberg commented on Jul 11, 2019

    @ahejlsberg
    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.

  7. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BugA bug in TypeScriptFixedA PR has been merged for this issue

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions