Skip to content

Wrong callback type inferred with multiple overloads #21525

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
aj-r opened this issue Feb 1, 2018 · 3 comments
Closed

Wrong callback type inferred with multiple overloads #21525

aj-r opened this issue Feb 1, 2018 · 3 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed Fix Available A PR has been opened for this issue

Comments

@aj-r
Copy link

aj-r commented Feb 1, 2018

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
@zuzusik
Copy link

zuzusik commented Feb 1, 2018

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

fn({ a: "a", b: "b" }, (value, key : string) => true);

@mhegazy
Copy link
Contributor

mhegazy commented Feb 1, 2018

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.

@mhegazy mhegazy added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Feb 1, 2018
@typescript-bot
Copy link
Collaborator

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

@microsoft microsoft locked and limited conversation to collaborators Jul 3, 2018
@typescript-bot typescript-bot added the Fix Available A PR has been opened for this issue label Feb 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed Fix Available A PR has been opened for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants