Repository navigation
proper overloading for arrow functionsย #47669
Description
Suggestion
๐ Search Terms
arrow function overload
โ Viability 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. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
โญ Suggestion
it seems that the syntax for overloading arrow functions are more like a side effect of existing functionality (function interfaces/intersections), rather than an intentional language feature. as a result, it doesn't really work properly in most cases - see #33482
it would be nice if there was actual syntax for arrow function overloading like there is for normal functions
๐ Motivating Example
const foo: {
(value: number): number
(value: string): string
} = (value) => valuethis is the most basic example of an overload i can think of, yet it doesn't work:
Type '(value: string | number) => string | number' is not assignable to type '{ (value: number): number; (value: string): string; }'.
Type 'string | number' is not assignable to type 'number'.
Type 'string' is not assignable to type 'number'.(2322)
as mentioned on #33482 (comment), function interfaces have a much broader purpose than overloads, meaning it has to protect against a potentially incorrect assignment in other scenarios. but here, that's obviously not what we want
perhaps a function overload type that looks something like:
const foo: overload {
(value: number): number
(value: string): string
} = (value) => value๐ป Use Cases
makes it easier to avoid using old function syntax
Activity
[-]proper overloading for arow functions[/-][+]proper overloading for arrow functions[/+]RyanCavanaugh commentedon Feb 1, 2022
This immediately runs into difficulty because how do you check the body?
neverand the function can't legally returnDetachHead commentedon Feb 1, 2022
how do overloads work on normal functions? can it just do it the same way?
nmain commentedon Feb 1, 2022
In the normal function case, there's a separate implementation signature which is not exposed to callers, and is allowed to be very weakly typed.
fatcerberus commentedon Feb 1, 2022
That's true Nicholai Main (@nmain), but the compiler does still check if the overloads are compatible with the implementation signature. If I read it right, the suggestion is, there would be dedicated syntax for an overload type like in the example given:
then the compiler could just use the overload compatibility check in lieu of an assignability check.
DetachHead commentedon Mar 21, 2022
an idea i came up with to solve the issue without adding any additional syntax is to improve the logic used to infer the type of the implementation when its types are omitted.
for example:
currently the value is inferred as
(value: string | number) => string | numberwhich is wrong. it should be instead inferred as<T extends string | number>(value: T) => T. that way you would get no errors on assignment, and you wouldn't have to do any casting in the implobviously this could get messy with some more complicated overloads though:
in this case perhaps there could be a built in
Overloadinterface to simplify the inferred type of the impl:then the impl can be inferred as something like
(though while playing around with this approach i ran into other issues such as #48345 and #46076)
icywolfy commentedon Dec 9, 2022
Bruce Pascoe (@fatcerberus)
Could the overload mechanism be similar to how function works?
Ryan Cavanaugh (@RyanCavanaugh) Would this syntax have any glaring issues?
erquhart commentedon Dec 26, 2022
Probably the biggest issue is that re-declaring constants isn't valid js. The example provided in the OP is viable, though.
icywolfy commentedon Dec 27, 2022
Shawn Erquhart (@erquhart) The existing function overload syntax in TypeScript declares functions without a body, which isn't valid syntax either. Invalid JS syntax is not a limitation.
DetachHead commentedon Dec 27, 2022
imo the example in the OP is better because you don't have to duplicate the function name nor do you have to add types to the implementation signature
erquhart commentedon Dec 27, 2022
Yeah, fair - I was more thinking of js support for declaring functions that have already been declared, and doing so with constants feels a bridge further. But yeah, not materially different since neither is really valid, agreed. Your syntax is honestly preferable if using
constthis way isn't an issue.craigphicks commentedon Jan 6, 2024
Regardless of the syntax for arrow function overload, I can see no reason not to use the same algorithm which is used for a function overload declaration.
It's true that the function declaration algorithm would be better if the return values in the implementation were type checked against the signatures in context. We can even simulate it in user code:
The obvious solution then is to unify the treatment of function declarations and assignment of arrow functions to overloads types,
and ALSO upgrade BOTH with the above return value checking. This same reasoning applies to generic functions as well.
Setting the return type of the implementation with the intersection of return types just breaks the existing type checking (which although not perfect is still better than nothing).
Also, the implementation signature could be reduced to parameter names only:
because it can be derived from the overloads:
readonly/constfunction implementation variables would help immensely with type checking function implementation return types.Note: It would be helpful for type checking the return types if there were a way to declare the parameter names as constant. This makes flow analysis far easier. (Note only overloads, but all functions).
Even if parameters type are declared as
readonlythe unloaded
a,b,c,dare NOT const.The only way to make use of the constness is to access the parameters are an array (or record)
but that is unnatural for the user. This would be better:
The left hand side
constis an instruction for the implemeter and implementation type checking.The right hand side
readonlyis an instruction for the caller and type checking of the call.The left hand side
constimplies the right hand sidereadonly, so it should be an error to have lhsconstwithout rhsreadonly.Applies to objects too: