Repository navigation
Method overloads are often frustrating for me #40827
Copy link
Copy link
Closed
Labels
Design LimitationConstraints of the existing architecture prevent this from being fixedConstraints of the existing architecture prevent this from being fixed
Description
TypeScript Version: 4.0.x
Search Terms: overload
Code
enum SideBySideEditor {
PRIMARY,
SECONDARY,
BOTH
}
interface URI { }
interface IEditorInput { }
interface IResourceOptions {
supportSideBySide?: SideBySideEditor;
}
function toResource(editor: IEditorInput | undefined | null): URI | undefined;
function toResource(editor: IEditorInput | undefined | null, options: IResourceOptions & { supportSideBySide?: SideBySideEditor.PRIMARY | SideBySideEditor.SECONDARY }): URI | undefined;
function toResource(editor: IEditorInput | undefined | null, options: IResourceOptions & { supportSideBySide: SideBySideEditor.BOTH }): URI | { primary?: URI, secondary?: URI } | undefined;
function toResource(editor: IEditorInput | undefined | null, options?: IResourceOptions): URI | { primary?: URI, secondary?: URI } | undefined {
// Method implementation left out for brevity.
return undefined;
}
export function getOriginalUri(editor: IEditorInput | undefined | null): URI | undefined;
export function getOriginalUri(editor: IEditorInput | undefined | null, options: IResourceOptions & { supportSideBySide?: SideBySideEditor.PRIMARY | SideBySideEditor.SECONDARY }): URI | undefined;
export function getOriginalUri(editor: IEditorInput | undefined | null, options: IResourceOptions & { supportSideBySide: SideBySideEditor.BOTH }): URI | { primary?: URI, secondary?: URI } | undefined;
export function getOriginalUri(editor: IEditorInput | undefined | null, options?: IResourceOptions): URI | { primary?: URI, secondary?: URI } | undefined {
// What I would like to type:
// return toResource(editor, options);
// What I have to type
if (!editor) {
return undefined;
}
if (!options) {
return toResource(editor);
}
if (options.supportSideBySide === SideBySideEditor.BOTH) {
return toResource(editor, options);
}
if (options.supportSideBySide === SideBySideEditor.PRIMARY || options.supportSideBySide === SideBySideEditor.SECONDARY) {
return toResource(editor, options);
}
}Expected behavior:
I can call the one method with overloads from the other without having to do a lot of checks.
Actual behavior:
I am often running into a problem when dealing with methods that define different overloads based on the types passed in. This works great if the method is called from the outside, but if internally I split the method up into 2 (as in the example above) I fail to call the one from the other somehow. I need to either duplicate all the code or do a lot of type checks in the second method that I will have to do again in the first method.
If there is guidelines how to do this any better, that would also help me.
Playground Link: Playground
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Design LimitationConstraints of the existing architecture prevent this from being fixedConstraints of the existing architecture prevent this from being fixed
Activity
MartinJohns commentedon Sep 29, 2020
Duplicate of #39885 / #1805.
RyanCavanaugh commentedon Sep 29, 2020
I think you should really have a third overload on
toResource:The thing to remember is that the implementation signature isn't visible (this is intentional) - in many cases (but not all, which is why this is intentional), you do need to write that signature "twice" (once as an overload, once as implementation). Without that last signature, this API design makes very little sense IMO, which is also why TS has a hard time reasoning about it.
bpasero commentedon Sep 29, 2020
Ryan Cavanaugh (@RyanCavanaugh) thanks, I learned something. Was not aware that the implementation signature is hidden.
DanielRosenwasser commentedon Oct 1, 2020
Back to #4797. 😢
opl- commentedon Oct 5, 2020
Here's an especially frustrating and simple example of what I believe to be the same issue, which just happened to me:
The error displayed on
createIfMissing:What makes it ridiculous is that if the
createIfMissinghas to be eithertrueorfalse, then why should I be unable to pass it into a function that can take in either of those values?Thanks to Ryan Cavanaugh (@RyanCavanaugh)'s comment above mentioning that the implementation signature is hidden, I actually managed to come up with a workaround for this specific issue while explaining it above, so here it is in case anyone else has the same issue:
Instead of declaring two specific overloads, in this instance you can simply declare one specific and one more general overload, and TypeScript should use the more specific one when possible. In practice:
Additionally, if you want your implementation to have a default value, you will need to explicitly mark the argument as optional in the overload, or TypeScript will require that all calls provide all arguments: