Skip to content

Enable Inferring from Overloaded FunctionsΒ #60836

Open
@LukeAbby

Description

πŸ” Search Terms

inferring, function overload

βœ… Viability Checklist

⭐ Suggestion

Make it possible to infer an overloaded function when trying to assign to one. This would make it more convenient to reuse overloaded functions.

When the function has explicitly typed inputs and outputs it would be able to reuse the same logic that's used to check if an implementation signature of an overloaded function is compatible with its overload signatures. When an argument or return type needs to be inferred then a union of all possible values is probably what should be inferred. This is already partially implemented.

This proposal is complimentary to other proposals like #59350 or #54539 but it's intentionally more limited in scope to some others. I'm aware of the overlap but I see this as a separate proposal because it's a fix for specifically the inference side of things, rather than anything more broad.

πŸ“ƒ Motivating Example

Currently this is what happens when you try:

interface SomeLongOverload {
    (x: string): string;
    (y: number | number[]): number[];
    (z: string[]): string;
    (a: number, b?: number): [number, number];
}

declare class BaseClass {
    someLongOverload1: SomeLongOverload;
    someLongOverload2: SomeLongOverload;
}

class SubClass extends BaseClass {
    someLongOverload1: SomeLongOverload = (a) => { ... }
    //              ^                      ^ This infers correctly as `string | number | number[] | string[] | number`
    //              ^
    //              ^ Type '(a: string | number | number[] | string[]) => string | number[] | string | [number, number]' is not assignable to type 'SomeLongOverload'.
    //                  Type 'string | number[] | [number, number]' is not assignable to type 'string'.
    //                    Type 'number[]' is not assignable to type 'string'.

    someLongOverload2: SomeLongOverload = (a, b) => { ... }
    //              ^                      ^  ^ infers as `number | undefined`
    //              ^                      ^
    //              ^                      ^ infers as `number`
    //              ^
    //              ^ Type '(a: number, b: number | undefined) => string | number[] | string | [number, number]' is not assignable to type 'SomeLongOverload'.
    //                  Target signature provides too few arguments. Expected 2 or more, but got 1.
}

Playground

Ideally neither of these examples would error and the second example would infer correctly. It seems that the current way inference works is to extract the types from all overloads with the same arity or greater. Given the nature of function overloads this should probably be changed.

If there are concerns with automatically inferring overloads in the general case, e.g. for something like takesLongOverloadedFunction((a, b) => { ... }) then it could be limited to cases of the form const x: SomeLongOverload = (...) => ...;. I can understand arguments both directions for takesLongOverloadedFunction because it could easily mask obvious errors.

πŸ’» Use Cases

  1. What do you want to use this for? Being more DRY
  2. What shortcomings exist with current approaches? Duplication of code. Increased difficulty to refactor.
  3. What workarounds are you using in the meantime? Being much more verbose.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions