Skip to content

Wrong callback type inferred with multiple overloads #21525

Description

@aj-r

TypeScript Version: 2.8.0-dev.20180131

Search Terms: callback type inference, incorrect overload

Code

interface TestFunction {
    <T>(input: { [key: number]: T }, callback: (value: T, key: number, collection: { [key: number]: T }) => boolean): boolean;
    <T extends object>(input: T, callback: (value: T[keyof T], key: string, collection: T) => boolean): boolean;
}

const fn: TestFunction = {} as any;
fn({ a: "a", b: "b" }, (value, key) => true);

Expected behavior: Code should compile successfully

Actual behavior: Compile error:

src\test.ts (10,32): Argument of type '(value: {}, key: number) => boolean' is not assignable to parameter of type '(value: string, key: string, collection: { a: string; b: string; c: string; }) => boolean'.
  Types of parameters 'key' and 'key' are incompatible.
    Type 'string' is not assignable to type 'number'. (2345)

Related Issues: DefinitelyTyped/DefinitelyTyped#23317

Notes:

  • The code above works in typescript 2.4. It seems to have broken in 2.5 and later (including 2.8).
  • It appears as if it's inferring the callback type based on the first overload, then failing to match it to the second overload.
  • The generic types seem necessary to reproduce the issue. I tried removing them and the issue went away.
  • If I explicitly pass the generic type argument, the code compiles successfully, i.e:
    fn<{ a: "a", b: "b" }>({ a: "a", b: "b" }, (value, key) => true);
  • If I remove the second callback parameter, the code compiles successfully, i.e:
    fn({ a: "a", b: "b" }, (value) => true);
  • Enabling/disabling strictFunctionTypes does not affect the result

Activity

  1. zuzusik commented on Feb 1, 2018

    @zuzusik

    another workaround here would be to specify key type as string explicitly:

    fn({ a: "a", b: "b" }, (value, key : string) => true);
  2. mhegazy commented on Feb 1, 2018

    @mhegazy
    Contributor

    This is a design limitation of how overload interacts with context sensitive lambdas (i.e. lambdas whose parameters are contextually typed). The overload resolution algorithm proceeds from the first overload, and tries to make inferences from the non-lambda inferences, then if that fails, it proceeds to infer from the lambdas, but since they are not typed, they need to get a type from context, so the type inferred so far is used. on the next overload, the types of the parameters of the lambda has already been set from the first overload, and has no way to undo the type application from earlier.

    Conditional types (#21316) should allow you to consolidate the two overloads into one.

  3. typescript-bot commented on Feb 20, 2018

    @typescript-bot
    Contributor

    Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

  4. locked and limited conversation to collaborators on Jul 3, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixedFix AvailableA PR has been opened for this issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions