You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Suggestion: Compile time function overloading #3442
As we know JavaScript doesn't support overloaded functions, it's when a few functions with different signatures share the same name within the same scope where they are defined. Overloading can still be achieved at runtime by checking arguments and dispatching the execution to a proper path.
TypeScript seems capable of doing static function overloading which can be resolved at the compile time. Here is one way of how it can be done:
a notion of a nominal and effective name of a function is required
a nominal name is an identifier which is used to name a function and reference it in the TypeScript code
an effective name is an identifier which is used to name a function and reference it in the JavaScript code
an effective name is optional, if omitted then the effective name should be the same as the nominal name
a nominal name is a subject for overloading and hence can be shared across more than one function within the same scope
an effective name has to be unique withing the scope
the developer is in charge for specifying both nominal and effective names
the TypeScript emitter should only use the effective names for generating the JavaScript code
a resolution of a nominal name to an effective name should be based on the signature information
the hypothetical syntax (TBD) for a function declaration utilizing both the effective and nominal name might look like the following:
Perhaps Typescript should just pick the effective name? A human would likely pick something similar anyways and part of the point of overloading is not having to think of a different name.
Jason Chen (@jhchen), although you are right that the compiler is able to generate synthetic effective names, i don't think it will contribute to the readability of the generated code, saving a little hassle with giving nams isn't worth less readable code
If TS wants to be only transition to ES6+, then let's do what ES6+ spec says in that area.
If TS wants to be somethng more than that, this is very helpful as many other features that are available to the moment of generating JS.
Yes! I want language that has more features that JS(whatever), so please add as many as possible options that helps develop applications!
Why have overloading at all if you still have to pick another name? The whole point of overloading is addressing the problem that the name the programmer wants is already being used. Requiring an effective and nominal name not only does not solve this problem, it introduces new syntax and unintuitive syntax purely for the benefit of generated code (which people read less and less given source maps). Also the generated names I suggested is very readable, especially considering generated code is most read during debugging and the prevalence of anonymous functions in Javascript. Sure it's less readable than a handpicked name by a human but this is precisely the burden that overloading is supposed to free us from.
In short, source code readability and developer productivity should be prioritized over generated code readability.
Jason Chen (@jhchen) inconvenience of picking an extra name is a one-time thing whereas being able to address different functions by the same name is a repeatable positive experience
requiring an extra name does solve the problem
it's one of the primary goals of typescript to keep generated javascript readable
agree, the syntax might or might not be intuitive, subject for discussion
generated names that you have suggested do not account for multiple arguments whose types might be extra awkward and ugly
in short
the developer has to be in charge for giving names
inconvenience is mild and one time thing
primary goals of typescript are met
to kill both birds:
both nominal and effective name can be specified
effective name can be optional
if a nominal name is reused but effective name is omitted then a synthetic effective name is generated
The thing that strikes me about this is, what problem are you trying to solve?
The disadvantages of perpetuating this "overloading" into JavaScript is that it increases the emitted code footprint dramatically and emits items that are very hard to optimise out in a subsequent build step in another tool chain. Why are you trying to place this unnecessary overhead on every other user of TypeScript so you can just perpetuate 100% of the language features of TypeScript into JavaScript?
This is probably the source of disagreement. In my experience I’ve found it to be true that naming is one of the few hard problems in computer science.
the syntax might or might not be intuitive, subject for discussion
Agree this is subjective. In the current form it resembles namespace syntax in other languages but agree the specifics could be discussed. However, I’m not aware of any other language has a syntax specification purely for the benefit of generated code, and since intuition is informed by experience, in my opinion the idea of a nominal+effective name itself is also unintuitive.
primary goals of typescript are met (readability of the generated code)
This should be weighed against other primary goals like developer productivity and source code maintainability.
effective name can be optional
This may be the right compromise but it’s worth noting this path is still not free. There’s still the implementation effort for the Typescript team, the subsequent documentation and support, and the cognitive overhead for the developers that read the manual in full.
But if this is the desirable route then I think this can be two separate Github Issues? One for function overloading supported by compiler generated names, and another Issue for the optional nominal+effective syntax to control the generated names.
naming is one of the few hard problems in computer science
agree, good and consistent naming is a hard thing to come by (that's why we are here talking about this feature), however not-as-good yet meaningful names aren't too hard to make up, and this is what handpicked effective names are, just good enough for the job to get done
since intuition is informed by experience
i am glad we pay so much attention to very personal subjective things, let's ask your experience one more time, how many languages do you know that proclaim readable generated code as one of their goals?
this path is still not free
i am with you on that, didn't you just say that naming is one of the hardest problems? all right.. so do you truly believe that using names provided by the developer is more work than developing a heuristic algorithms for making good looking names automatically?
by saying so, i conclude, you must have already developed one for all crazy signatures you can see in the wild:
// if there is a common (comparible?) signature between someOtherFunction and one of the overloads// then x is of the type of such signature, otherwise we get an unresolved type errorvarx=someCondition ? format : someOtherFunction;x("");x(32);functionfn(x: any){/* some body */}fn(format);// <-- type error, resolving from nominal to effective is based on the signature which is erased herevary: any=someCondition ? "" : 32;format(y);// <-- if there is an overload for `string|number` no problem, otherwise a type error/// UPDATE: didn't see `any`, if there is an overload for `any` then we use it, otherwise a type error
Kitson Kelly (@kitsonk) The code path is entirely unambiguous; the param types are part of a function's signature and serve to disambiguate resolution. I.e. a call to alertSomething(x: number) results in a call to alertSomething(x: string) which in its body does alert(x: string).
In your example, the answer is it's a compile time error—unable to resolve which overload to use. This is perfectly reasonable and is the behavior in many languages which allow this practice. If you were so inclined, then you could disambiguate your call with the foobar object by typing it in an unambiguous way, such as assigning it to a var explicitly typed as a Foo, or an Object, or what have you.
It would be difficult to approach renaming the overloading functions as someone else in this thread suggested because external things could be relying on the one name. I think the best way to do it would be to write out exactly what a human has to write now in order to do this runtime "pseudo-overloading" and keep the original method name so no external references to the symbol need to be changed, e.g.:
// Theoretical TypeScript as before
function alertSomething(str: string): void {
alert(str);
}
function alertSomething(num: number): void {
alertSomething(num.toString());
}
Then of course this could be extended so that number of arguments could be considered, etc. This is a viable solution because you can compile that down right there into the resultant JavaScript without having knowledge of external callers using whichever overloads (since the symbol name ultimately stays the same), and ultimately the end user (programmer) would end up writing exactly that sort of runtime type checking anyway.
We had some discussion in the slack chat of https://angularjs-de.slack.com (Typescript chan) I used GWT for a long time and I wondered by myself, why is real overloading in TS not possible, but in GWT. Both generated JS code. So someone came up with an idea, how GWT could do this (Only an idea)
So it would be very handy, if we can have this in TypeScript too. I heard about the philosophy to not generate stuff which is not supported in JS at all, but if you generate the code, concate the files, minify the files (e.g. for a spa) than you don't want to read the code which was generated anymore. You only write in TS.
Why require the dispatch to be static? It would not fit into JS world.
We also don't have to generate the code for dynamic dispatch. The programmer already doing it.
Yet, IMHO we can still have better type-checking and validations over overloaded functions: #12041
Why can't function overloading simply be implemented as syntactic sugar for the regular single function with the standard pattern of typeof/instanceof checking?
i.e.
function foo(x: number){/*body Foo 1*/}
function foo(x: string, y: number){/*body Foo 2*/}
obviously, the emit isn't beautiful, but it's certainly readable and is currently what is emitted for function(...args) anyway. An alternative would be to boil down an overloaded function into the equivalent Typescript function defined with optionals and unions
i.e.
function foo(x: number){/*body Foo 1*/}
function foo(x: string, y: number){/*body Foo 2*/}
would be equivalent to
function foo(x: number | string, y?: number) {
if (typeof x === "number" && y === null) {
/* Foo body 1*/
}
else if (typeof x === "string" && y != null) {
/* Foo body 2*/
}
}
which emits to
function foo(x, y) {
if (typeof x === "number" && y === null) {
/* Foo body 1*/
}
else if (typeof x === "string" && y != null) {
/* Foo body 2*/
}
}
Am I oversimplifying this issue? Obviously making Typescript enforce typings and usage of the overloaded functions in IDE and compile time is a different problem, but with my limited knowledge, it seems like a half solved problem?
Ultimately, your scenario would only work when primitive values are used as arguments. Also, you suggestion doesn't cater for overloading of return types.
You know, the use of get property and set property show that this method overloading enhancement can be done. Not terribly difficult an enhancement for TypeScript language.
You know, the use of get property and set property show that this method overloading enhancement can be done.
You know, that is valid JavaScript syntax, versus some special TypeScript construct. That isn't function overloading, that is interpreting JavaScript syntax.
As Mohamed stated above it isn't the technical difficulty, it is that it break multiple design goals and non-goals of TypeScript.
Activity
jhchen commentedon Jun 10, 2015
Perhaps Typescript should just pick the effective name? A human would likely pick something similar anyways and part of the point of overloading is not having to think of a different name.
Given your example:
Typescript could keep track of unique function signatures and append an index in the generated Javascript:
Or it could pick more descriptive names by appending the type instead of the index:
A downside of option 2 is the names could be very long for some functions.
zpdDG4gta8XKpMCd commentedon Jun 11, 2015
Jason Chen (@jhchen), although you are right that the compiler is able to generate synthetic effective names, i don't think it will contribute to the readability of the generated code, saving a little hassle with giving nams isn't worth less readable code
wgebczyk commentedon Jun 11, 2015
If TS wants to be only transition to ES6+, then let's do what ES6+ spec says in that area.
If TS wants to be somethng more than that, this is very helpful as many other features that are available to the moment of generating JS.
Yes! I want language that has more features that JS(whatever), so please add as many as possible options that helps develop applications!
jhchen commentedon Jun 11, 2015
Why have overloading at all if you still have to pick another name? The whole point of overloading is addressing the problem that the name the programmer wants is already being used. Requiring an effective and nominal name not only does not solve this problem, it introduces new syntax and unintuitive syntax purely for the benefit of generated code (which people read less and less given source maps). Also the generated names I suggested is very readable, especially considering generated code is most read during debugging and the prevalence of anonymous functions in Javascript. Sure it's less readable than a handpicked name by a human but this is precisely the burden that overloading is supposed to free us from.
In short, source code readability and developer productivity should be prioritized over generated code readability.
zpdDG4gta8XKpMCd commentedon Jun 11, 2015
Jason Chen (@jhchen) inconvenience of picking an extra name is a one-time thing whereas being able to address different functions by the same name is a repeatable positive experience
requiring an extra name does solve the problem
it's one of the primary goals of typescript to keep generated javascript readable
agree, the syntax might or might not be intuitive, subject for discussion
generated names that you have suggested do not account for multiple arguments whose types might be extra awkward and ugly
in short
to kill both birds:
kitsonk commentedon Jun 11, 2015
The thing that strikes me about this is, what problem are you trying to solve?
The disadvantages of perpetuating this "overloading" into JavaScript is that it increases the emitted code footprint dramatically and emits items that are very hard to optimise out in a subsequent build step in another tool chain. Why are you trying to place this unnecessary overhead on every other user of TypeScript so you can just perpetuate 100% of the language features of TypeScript into JavaScript?
zpdDG4gta8XKpMCd commentedon Jun 11, 2015
can't see how naming without overloading that i originally suggested (with handpicked effective name) is better as far as the size of footprint?
can you elaborate on this?
jhchen commentedon Jun 11, 2015
This is probably the source of disagreement. In my experience I’ve found it to be true that naming is one of the few hard problems in computer science.
Agree this is subjective. In the current form it resembles namespace syntax in other languages but agree the specifics could be discussed. However, I’m not aware of any other language has a syntax specification purely for the benefit of generated code, and since intuition is informed by experience, in my opinion the idea of a nominal+effective name itself is also unintuitive.
This should be weighed against other primary goals like developer productivity and source code maintainability.
This may be the right compromise but it’s worth noting this path is still not free. There’s still the implementation effort for the Typescript team, the subsequent documentation and support, and the cognitive overhead for the developers that read the manual in full.
But if this is the desirable route then I think this can be two separate Github Issues? One for function overloading supported by compiler generated names, and another Issue for the optional nominal+effective syntax to control the generated names.
zpdDG4gta8XKpMCd commentedon Jun 12, 2015
agree, good and consistent naming is a hard thing to come by (that's why we are here talking about this feature), however not-as-good yet meaningful names aren't too hard to make up, and this is what handpicked effective names are, just good enough for the job to get done
i am glad we pay so much attention to very personal subjective things, let's ask your experience one more time, how many languages do you know that proclaim readable generated code as one of their goals?
i am with you on that, didn't you just say that naming is one of the hardest problems? all right.. so do you truly believe that using names provided by the developer is more work than developing a heuristic algorithms for making good looking names automatically?
by saying so, i conclude, you must have already developed one for all crazy signatures you can see in the wild:
if so you should not hesitate and include it as a part of the formal proposal
RyanCavanaugh commentedon Jun 12, 2015
Can you fill in the emit for this block?
zpdDG4gta8XKpMCd commentedon Jun 12, 2015
No problem:
13 remaining items
evictor commentedon Feb 26, 2016
Kitson Kelly (@kitsonk) The code path is entirely unambiguous; the param types are part of a function's signature and serve to disambiguate resolution. I.e. a call to alertSomething(x: number) results in a call to alertSomething(x: string) which in its body does alert(x: string).
In your example, the answer is it's a compile time error—unable to resolve which overload to use. This is perfectly reasonable and is the behavior in many languages which allow this practice. If you were so inclined, then you could disambiguate your call with the foobar object by typing it in an unambiguous way, such as assigning it to a var explicitly typed as a Foo, or an Object, or what have you.
kitsonk commentedon Feb 26, 2016
Ezekiel Victor (@evictor) if it is unambiguous, what would you propose as an emit?
evictor commentedon Feb 27, 2016
It would be difficult to approach renaming the overloading functions as someone else in this thread suggested because external things could be relying on the one name. I think the best way to do it would be to write out exactly what a human has to write now in order to do this runtime "pseudo-overloading" and keep the original method name so no external references to the symbol need to be changed, e.g.:
Then of course this could be extended so that number of arguments could be considered, etc. This is a viable solution because you can compile that down right there into the resultant JavaScript without having knowledge of external callers using whichever overloads (since the symbol name ultimately stays the same), and ultimately the end user (programmer) would end up writing exactly that sort of runtime type checking anyway.
ToastHawaii commentedon Feb 27, 2016
This is dangeres if you have varibales mit the same name in the function.
I suggest:
Or
Chris2011 commentedon May 30, 2016
We had some discussion in the slack chat of https://angularjs-de.slack.com (Typescript chan) I used GWT for a long time and I wondered by myself, why is real overloading in TS not possible, but in GWT. Both generated JS code. So someone came up with an idea, how GWT could do this (Only an idea)
Java (Used in GWT)
JavaScript (Output)
Using:
Java (Used in GWT)
JavaScript (Output)
And this is similar to this comment: #3442 (comment)
So it would be very handy, if we can have this in TypeScript too. I heard about the philosophy to not generate stuff which is not supported in JS at all, but if you generate the code, concate the files, minify the files (e.g. for a spa) than you don't want to read the code which was generated anymore. You only write in TS.
mhegazy commentedon Jun 4, 2016
This proposal violates multiple of the TypeScript design goals (https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals#goals); specifically. breaking changes (11), changing behavior of a JS program (7), and, though a subjective argument, generates not-pretty-code (4).
The other issue is it relies on type-directed emit. something that would break single file transpilation scenarios.
for all of these, this proposal is out of scope for the TypeScript project for the time being.
Shlomibo commentedon Jan 13, 2017
Why require the dispatch to be static? It would not fit into JS world.
We also don't have to generate the code for dynamic dispatch. The programmer already doing it.
Yet, IMHO we can still have better type-checking and validations over overloaded functions:
#12041
G1itcher commentedon Nov 25, 2017
Why can't function overloading simply be implemented as syntactic sugar for the regular single function with the standard pattern of typeof/instanceof checking?
i.e.
emits to
obviously, the emit isn't beautiful, but it's certainly readable and is currently what is emitted for function(...args) anyway. An alternative would be to boil down an overloaded function into the equivalent Typescript function defined with optionals and unions
i.e.
would be equivalent to
which emits to
Am I oversimplifying this issue? Obviously making Typescript enforce typings and usage of the overloaded functions in IDE and compile time is a different problem, but with my limited knowledge, it seems like a half solved problem?
kitsonk commentedon Nov 26, 2017
G1itcher that would work for a very limited use cases. What about something like this?
Ultimately, your scenario would only work when primitive values are used as arguments. Also, you suggestion doesn't cater for overloading of return types.
fletchsod-developer commentedon Nov 27, 2017
You know, the use of
get propertyandset propertyshow that this method overloading enhancement can be done. Not terribly difficult an enhancement for TypeScript language.kitsonk commentedon Nov 27, 2017
You know, that is valid JavaScript syntax, versus some special TypeScript construct. That isn't function overloading, that is interpreting JavaScript syntax.
As Mohamed stated above it isn't the technical difficulty, it is that it break multiple design goals and non-goals of TypeScript.