Reputation: 96556
I came across this code VSCode:
export class HistoryService extends Disposable implements IHistoryService {
...
remove(input: IEditorInput | IResourceEditorInput): void;
remove(input: FileChangesEvent): void;
remove(arg1: IEditorInput | IResourceEditorInput | FileChangesEvent): void {
...
}
What is the point of the first two remove()
definitions? Aren't they a subset of the last line?
Upvotes: 1
Views: 154
Reputation: 51034
There is a semantic difference between a function with overloads vs. a function with a parameter having a union type. Consider the following code:
interface A { a: number }
interface B { b: number }
// overloads
function f(a: A): void;
function f(b: B): void;
function f(ab: A | B): void {
// ...
}
// union type parameter
function g(ab: A | B): void {
// ...
}
let ab: A | B = Math.random() > 0.5 ? { a: 1 } : { b: 2 };
f(ab); // error
g(ab); // no error
In this code, f
can be called with an argument of type A
or an argument of type B
, but not an argument of type A | B
, because A | B
does not conform to either overload signature. On the other hand, g
can be called with an argument of type A | B
because that exactly matches its parameter type.
So in your example, the reason for using overloads may be to signal that an argument should not have the type IEditorInput | IResourceEditorInput | FileChangesEvent
, perhaps because the caller is expected to know whether they are calling the function with an "input" or an "event".
Upvotes: 4
Reputation: 1074218
They're function overloads. The first two signatures are the public signatures of the function; the third is its implementation.
In the general case, they can make it much clearer what a function is expecting. It's true that in the specific case you've shown in the question, you could just write the third signature; the overloads aren't buying you anything. But perhaps doing it this way makes the intent clearer when auto-completion options are shown, etc.
Here's a better example of where overloads help clarity:
interface OptionsObject {
foo: string;
bar: number;
somethingElse: Date;
}
function example(foo: string, bar: number, somethingElse: Date): void;
function example(options: OptionsObject): void;
function example(arg0: string | OptionsObject, bar?: number, somethingElse?: Date): void {
// ...implementation...
}
The overloads make it clear that you can call that function in one of two ways:
In contrast, the implementation signature doesn't make it clear that calling it with just a string argument and nothing else isn't valid. To make that clear, you need the overloads.
But again, not with the example you cited. It's not clear to me why overloads were used there (unless the code used to be different and has changed over time).
Upvotes: 3