Description
π Search Terms
inferring, function overload
β Viability Checklist
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
β 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.
}
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
- What do you want to use this for? Being more DRY
- What shortcomings exist with current approaches? Duplication of code. Increased difficulty to refactor.
- What workarounds are you using in the meantime? Being much more verbose.
Activity