##### Updated: February 2, 2021

Take a function like below…

type Combinable = string | number

function add(a: Combinable, b: Combinable): Combinable {
if (typeof a === 'string' || typeof b === 'string') {
return a.toString() + b.toString()
}
return a + b
}


If we were to use this function like this:

const x = add('black cat', 'white dog')
x.split(' ') // TS compiler will complain here


TypeScript cannot determine what the return type of add is at this point. All it knows is that it might be a string | number. We could cast it as a known type (i.e. as string), but this is unnecessary and more code than we should really have to write.

In order to “educate” the compiler we can overload the function like so:

function add(a: number, b: number): number
function add(a: string, b: string): string {
// rest of function
}


where we placed the various types the function could take immediately above the function definition. The extra function signatures will be removed at compile time. Now, when we want to use the function as done above, .split(' ') will not throw a compilation error because the compiler can now determine the exact return type based on the types of the arguments.