Repository navigation
Suggestion: Automatically infer argument types in overloaded function implementation #28172
Description
Search Terms
infer, arguments, function, overload
Suggestion
For basic container types (Point, Size, Rect, ...) I often write constructors and methods with overloaded signatures so they can be called with the container type itself or with the separate components of the container as arguments. Example:
class Point {
constructor(point: Point);
constructor(x: number, y: number);
constructor(arg1: Point | number, arg2?: number) {
if (arg1 instanceof Point) {
this.x = arg1.getX();
this.y = arg1.getY();
} else {
this.x = arg1;
this.y = arg2!;
}
}
}And I always wonder why I have to specify the argument types in the implementation again when I already defined the possible types in the overload signatures.
In this simple Point type it is still pretty easy but imagine a Rect type which can work with four number arguments, two Point arguments, a Point and Size argument or a Rect argument. Manually writing the combined signature for all these overloaded signatures is cumbersome. And I don't want to use any here because I want type checking in the function body.
An alternative way to write this example is this:
class Point {
constructor(point: Point);
constructor(x: number, y: number);
constructor(...args: [ Point ] | [ number, number ]) {
const [ arg1, arg2 ] = args;
// arg1 is now Point | number
// arg2 is now number | undefined (At least since TS 3.2 because of #27543)
...
}
}This shows how easy it should be for TypeScript to collect the possible function signatures into a union type so writing constructor(...args) would be enough.
Taking this a step further I even like to write this so I don't need to destructure the arguments myself:
class Point {
constructor(point: Point);
constructor(x: number, y: number);
constructor(arg1, arg2) {
// arg1 is now Point | number
// arg2 is now number | undefined
...
}
}Taking this ANOTHER step further TypeScript could even narrow down the inferred function signatures by each type check done within the function body:
class Point {
constructor(point: Point);
constructor(x: number, y: number);
constructor(arg1, arg2) {
if (arg1 instanceof Point) {
// arg2 can now only be `undefined` because the instanceof check removes
// the second call signature (where arg1 is a number) from the list of possible
// call signatures
this.x = arg1.getX();
this.y = arg1.getY();
} else {
// arg2 can now only be `number` because the failed instanceof check removes
// the first call signature from the list of possible signatures
this.x = arg1;
this.y = arg2;
}
}
}I guess the type narrowing is harder to implement but at least the automatic type inference of each argument shouldn't be that hard. So it would be very nice if a future version of TypeScript could do this so using overloading gets a bit easier.
Checklist
My suggestion meets these guidelines:
- 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. new expression-level syntax)
Activity
weswigham commentedon Oct 26, 2018
Duplicate of #25352 ?
pathurs commentedon Nov 11, 2018
Wesley Wigham (@weswigham) This is not a duplicate. This issue is about inferring the overloads of a function automatically.
This could be taken further, and include adding to the
argumentswithin the function.Jamesernator commentedon Apr 15, 2019
There are two parts to this:
More clearly, we can get this far today already:
Which works as expected, inferring the type of
argsin this example should be pretty easy fulfilling 1 but 2 is harder.Fulfilling 2. would be equivalent to making this type correct:
How could this work? Well I can think of one way that would just involve de-sugaring destructuring at the type-level (but not runtime de-sugaring as the tuples might still be mutable) to be equivalent to the second
if-statement in that example that works. This way the variable's type could be context dependent rather than fixed at destructuring time.Interestingly if this technique could be made to work this wouldn't really be any harder to extend to work for all destructuring not just tuples. So existing discriminants could also be destructured with type inference just working:
ianstormtaylor commentedon Jun 9, 2020
Please add support for this. It's really frustrating when dealing with functions that have multiple overloads (eg. subsets of a base object) to have to duplicate the definitions when TypeScript could easily infer it for us.
daprahamian commentedon Feb 1, 2021
This feature would be pretty valuable when dealing with node-style callback:
runspired commentedon May 24, 2021
Honestly the absence of narrowing within the implementation makes overloads almost pointless for interfaces as anyone implementing struggles with the narrowing side.
KayBeSee commentedon Jan 16, 2022
I found this issue while trying to solve for a problem like this.
I would think a lot of other folks are trying to solve the same problem since this pattern is so popular. Is there a best practices for something like this?
Jamesernator commentedon Jan 18, 2022
So TS4.4 added control flow analysis on discriminants, I wonder if that support could be extended to tuples such that inferred types after narrowing.
Jamesernator commentedon Mar 1, 2022
So TS4.6 just added a limited version of this in way of inference of args for discriminated tuples. Hopefully we will see support for more generic signatures in future.