함수 오버로딩은 같은 이름의 함수를 매개변수의 타입이나 갯수를 추가 혹은 변경함으로써 여러 버전으로 사용할 수 있도록 해주는 문법입니다.
꼭 매개변수의 타입과 갯수만 다른 것이 아니고 반환값의 타입도 달라도 됩니다.
즉, 호출할 수 있는 함수의 타입을 미리 여러 개 정의할 수 있는 문법입니다.
// 버전들 -> 오버로드 시그니쳐
function func(a: number): void;
function func(a: number, b: number, c: number): void;
// 실제 구현부 -> 구현 시그니쳐
function func(a: number, b?: number, c?: number) {
if (typeof b === "number" && typeof c === "number") {
console.log(a + b + c);
} else {
console.log(a * 20);
}
}
func(1); // ✅ 버전 1 - 오버로드 시그니쳐
func(1, 2); // ❌
func(1, 2, 3); // ✅ 버전 3 - 오버로드 시그니쳐
위와 같이 func
이라는 이름의 함수는 number
타입의 매개변수 하나를 받거나 혹은 세 개를 받는 두 가지의 버전으로 동작할 수 있도록 하는 것입니다.
즉, 같은 이름의 함수가 상황별로 다르게 동작할 수 있도록 만드는 문법이 함수 오버로딩입니다.
함수의 몸체 없이 선언부만 만들어둔 함수를 오버로드 시그니처라고 합니다.
오버로드 시그니처를 만들었다면 이후로는 구현 시그니처를 만들어줘야 합니다.
구현 시그니처는 실제로 함수가 어떻게 실행될 것인지를 정의하는 부분입니다.
function add(x: number, y: number): number;
function add(x: string, y: string): string;
function add(x: any, y: any){
return x + y;
}
add(1, 2);
add('1', '2');
add(1, '2'); // 에러
add('1', 2); // 에러
위와 같은 코드는 다음과 같은 에러가 발생합니다.
function example(param: string): string;
function example(param: string | null): number;
function example(param: string | null): string | number {
if (param) {
return 'string';
} else {
return 123;
}
}
const result = example('what');
'what'
은 string
타입이기 때문에 1번과 2번 오버로드 시그니처에 모두 해당됩니다.
동시에 여러 오버로드 시그니처에 해당될 수 있는 경우는 제일 먼저 일치하는 오버로드 시그니처에 해당됩니다.
따라서 result
의 타입은 string
이며 값은 'string'이 됩니다.
function example(param: string | null): number;
function example(param: string): string;
function example(param: string | null): string | number {
if (param) {
return 'string';
} else {
return 123;
}
}
const result = example('what');
순서를 바꿔서 해보면 다른 결과가 나옵니다.
이번엔 result
의 타입이 number
가 됩니다. 하지만 값은 'string'이 됩니다.
따라서 문제가 생기지 않게 하기 위해서는 오버로드 시그니처 선언 순서는 좁은 타입부터 넓은 타입순으로 선언해야 합니다.
인터페이스를 이용해서도 오버로딩이 가능합니다.
interface Add {
(x: number, y: number): number;
(x: string, y: string): string;
}
const add: Add = (x: any, y: any) => x + y;
타입 별칭을 이용해서도 오버로딩이 가능합니다.
type Add1 = (x: number, y: number) => number;
type Add2 = (x: string, y: string) => string;
type Add = Add1 & Add2;
const add: Add = (x: any, y: any) => x + y;
각각 선언한 함수 타입을 인터섹션 타입으로 묶으면 오버로딩과 같은 역할을 합니다.