-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Suggestion: Automatically infer argument types in overloaded function implementation #28172
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
Comments
Duplicate of #25352 ? |
@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 class MyClass {
a?: number;
b?: string;
constructor(a: number): void;
constructor(b: string): void;
constructor(a: number, b: string): void;
constructor(): void {
if (arguments.length === 1) { // [string | number]
if (typeof arguments[0] === 'number') { // [number]
this.a = arguments[0]; // number
} else {
this.b = arguments[0]; // string
}
} else { // [number, string]
this.a = arguments[0]; // number
this.b = arguments[1]; // string
}
}
} |
There are two parts to this:
More clearly, we can get this far today already: class MyClass {
a?: number;
b?: string;
constructor(a: number): void;
constructor(b: string): void;
constructor(a: number, b: string): void;
constructor(...args: [number] | [string] | [number, string]): void {
if (args.length === 1) { // [string | number]
if (typeof args[0] === 'number') { // [number]
this.a = args[0]; // number
} else {
this.b = args[0]; // string
}
} else { // [number, string]
this.a = args[0]; // number
this.b = args[1]; // string
}
}
} Which works as expected, inferring the type of Fulfilling 2. would be equivalent to making this type correct: declare const z: ['a', A] | ['b', B]
const [type, value] = z
if (type === 'a') {
const a: A = value // Needs to be inferred as A
}
// This works already so it can be surprising/annoying that the destructuring equivalent doesn't work
if (z[0] === 'a') {
const a: A = z[1] // Already inferred to be A
} 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 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: declare const z: { type: 'a', a: A } | { type: 'b', b: B }
const { type, ...rest } = z
if (type === 'a') {
const a: { a: A } = rest // would be type-correct
} |
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. |
This feature would be pretty valuable when dealing with node-style callback: function checkNumber(x: number): void {}
function checkUndefined(x: undefined): void {}
function callback(err: number, val: undefined): void;
function callback(err: undefined, val: number): void;
function callback(err: number|undefined, val: number|undefined): void {
if (err == null) {
checkUndefined(err);
checkNumber(val); // would like to be able to infer that this is a number
} else {
checkUndefined(val); // would like to infer that this is undefined
checkNumber(err)
}
} |
Honestly the absence of narrowing within the implementation makes overloads almost pointless for interfaces as anyone implementing struggles with the narrowing side. |
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? |
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. |
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. |
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:
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 aRect
type which can work with four number arguments, twoPoint
arguments, aPoint
andSize
argument or aRect
argument. Manually writing the combined signature for all these overloaded signatures is cumbersome. And I don't want to useany
here because I want type checking in the function body.An alternative way to write this example is this:
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:
Taking this ANOTHER step further TypeScript could even narrow down the inferred function signatures by each type check done within the function body:
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:
The text was updated successfully, but these errors were encountered: