type Combinable = string | number;
function add(a: Combinable, b: Combinable) {
if (typeof a === "string" || typeof b === "string") {
return a.toString() + b.toString();
}
return a + b;
}
const val1 = add(10, 10); // string | number
const val2 = add("add", " function"); // string | number
위 코드에서 val1
, val2
각각 number, string이 나온다는것을 알 수 있지만, TypeScript는 위와같이 유니온 타입을 반환한다. 물론 이러한 경우, type declare특징을 활용할 수 있다. 하지만 이는 코드가 지저분해지는 결과를 가져온다
const val1 = add(10, 10) as number;
const val2 = add("add", " function") as string;
이러한 경우 함수 오버로딩을 통해 특정 타입의 매개변수가 함수에 입력되었을때 어떤 타입을 반환할지 명시할 수 있다.(근본적인 개념은 일반적인 객체지향 언어의 오버로딩과 비슷하다. 동일한 함수명을 가지고 있지만, 서로 다른 선언부를 가지고 있다.)
함수 오버로딩은 함수 선언부 위에 동일한 이름의 함수 시그니처를 작성한다. 단 주의할 점은 오버로딩 선언문은 함수 블록을 선언하지 않는다는 점이다.
주의할 점은 함수 오버로딩은 Native JavaScript Feature가 아니다. 그렇기 때문에, 컴파일시, 오버로딩 시그니처들은 모두 사라지게 된다
function add(a: string, b: string): string;
function add(a: number, b: number): number;
function add(a: Combinable, b: Combinable) {
if (typeof a === "string" || typeof b === "string") {
return a.toString() + b.toString();
}
return a + b;
}
위와 같이 오버로딩 하면 TypeScript는 입력 매개변수 타입에 맞는 오버로딩된 함수 시그니처를 선언문으로 사용하게 된다.
하지만 주의할 점이 있다.아래와 같이 실제 구현 함수 시그니처의 매개변수 타입이 포괄할 수 없는 타입의 경우에는 사용이 불가능하다. 아래 예시에서는 Combinable
타입은 string
,number
타입만 포괄이 가능하기에, boolean
타입이 되지 않는것이다.
function add(a: boolean, b: string); // 이 오버로드 시그니처는 해당 구현 시그니처와 호환되지 않습니다.
function add(a: Combinable, b: Combinable) {
if (typeof a === "string" || typeof b === "string") {
return a.toString() + b.toString();
}
return a + b;
}
실제 구현 함수 시그니처에는 매개변수가 2개지만, 매개변수가 1개인 함수 오버라이딩을 한다고 가정하자. 이런 경우에는, 실제 구현 함수 시그니처의 매개변수를 optional 매개변수로 선언을 변경해 주어야 한다. 아래 예시에서 보면 실제 구현 함수 시그니처의 두번째 매개변수를 optional 매개변수로 선언을 변경한것을 볼 수 있다.
물론 Optional 매개변수라는것은, nullish 하다는 의미이므로, 이에 대한 TypeGuard도 진행해 주어야한다.
function add(a: string): string;
function add(a: Combinable, b?: Combinable) {
// nullish b 에 대한 TypeGuard
if (b) {
if (typeof a === "string" || typeof b === "string") {
return a.toString() + b.toString();
}
return a + b;
}
return -1;
}
만약에 실제 구현 함수 시그니처보다 많은 매개변수를 가지고 있으면 어떨까? 개인적으로 좋은 방법은 아니라고 생각하지만, 최선의 방법은 Rest Parameter를 통해 받는 방법이라고 생각한다.
function add(a: string, b: string, c: number): string;
function add(a: Combinable, b?: Combinable, ...args: any[]) {
console.log(args);
// nullish b 에 대한 TypeGuard
if (b) {
if (typeof a === "string" || typeof b === "string") {
return a.toString() + b.toString();
}
return a + b;
}
return -1;
}
add("a", "b", 10);
/* console
[ 10 ]
*/