TypeScript에 대한 간단한 정리 - 09. 함수 - 오버로딩(Overloading)

2024. 12. 20. 18:13Programming Language/Node.js

타입스크립트에서 함수 오버로딩은 동일한 함수 이름으로 여러 가지 다른 형태의 함수 시그니처를 정의할 수 있게 해주는 기능으로 하나의 함수 이름으로 다양한 인자와 반환 값을 처리할 수 있다.

 

함수 오버로딩에 대해서 알아보기 전에 먼저 함수 시그니쳐에 대해서 알아봐야 하는데 함수 시그니쳐란 함수의 형태를 정의하는 것으로 함수의 이름, 매개변수의 타입, 반환값의 타입에 대해서 미리 정의해서 함수가 어떤 입력을 받고, 어떤 출력값을 반환하는지를 명시해주는 방식을 말한다.

 

이는 코드에서 발생할 수 있는 타입 오류를 사전에 방지할 수 있는데 함수 시그니쳐를 사용하는 방법은 

function 함수명(매개변수1: 타입1, 매개변수2: 타입2): 반환타입;

과 같이 선언해주면 된다.

 

이렇게 함수 시그니처를 선언한다면 이걸 구현하는 구현부를 만들어줘야지만 함수를 호출 할 수 있게 되는데 

function 함수명(매개변수1: 타입1, 매개변수2: 타입2): 반환타입{
	// 구현 코드
}

와 같이 작성한다.

 

이렇게 궂이 함수 시그니처를 사용하는 경우는 보통 함수 오버로딩, 인터페이스, 타입 정의 등의 상황에서 사용하기 위함인데 이전에 인터페이스 호출 시그니처 부분에서 했던것과 비슷하게 인터페이스 내부에서 

interface 인터페이스명 {
	함수명(매개변수1: 타입1, 매개변수2: 타입2): 반환타입;
}

과 같이 함수 시그니처를 선언하고 

const 변수명 : 인터페이스명 = {
	함수명(매개변수1: 타입1, 매개변수2: 타입2): 반환타입{
        // 구현 내용
    }
}

을 사용해서 사용할 수 있다 

 

함수 시그니처와 호출 시그니처의 다른점은 단독으로 시그니처를 사용해서 구현부를 생성이 가능한가와 함수명을 고정되게 명시하는가의 차이인듯 보인다.

호출 시그니처는 

interface 인터페이스명 {
	(매개변수명 : 매개변수 타입 ) : 반환타입;
}

type 타입명 {
	(매개변수명 : 매개변수 타입 ) : 반환타입;
}

과 같은 형태로 존재하여 인터페이스 혹은 type 타입을 갖는 객체에서 함수처럼 동작하는 부분의 구현을 생성하는데 반면 

함수 시그니처는 외부에 선언되어 독립적으로 함수를 생성하기 위한 방식으로 사용된다.

 

 

함수 시그니처는 함수 오버로딩에서 사용되는데 먼저 다수의 함수 시그니처를 정의한다.

// 함수 오버로딩 시그니처 정의
function add(a: number, b: number): number;  // 숫자 두 개를 더하는 함수
function add(a: string, b: string): string;  // 문자열 두 개를 합치는 함수

그리고 이 두 함수 시그니처를 모두 만족하는 함수를 정의하는데 그 방식은 

function 함수명 (매개변수명1 : any, 매개변수명2 : any): any{
	// 구현 코드
}

를 사용해서 함수명 / 매개변수명을 일치 시키고 모든 타입을 any로 하여 함수 시그니처를 모두 만족할 수 있도록 구현한다.

function add(a: any, b: any): any {
    return a + b;
}

 

중요한건 함수 구현부가 모든 함수 시그니처를 처리할 수 있도록 구현해야한다는 점이다.

이렇게 구현하면 매개변수를 어떤 타입을 넣느냐에 따라서 타입스크립트가 자동적으로 어떤 시그니처를 사용할 지 결정해서 실행시켜준다.

function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: any, b: any): any {
    return a + b;
}

const result1 = add(5, 10);  // 15, 숫자 더하기
const result2 = add('Hello', 'World');  // 'HelloWorld', 문자열 더하기

 

그렇기에 선택적으로 매개변수를 추가하는 방식으로 여러 방법으로 함수의 로직을 구현할 수 도 있다.

function concat(a: string, b: string): string;
function concat(a: string, b: string, c: string): string;
function concat(a: string, b: string, c?: string): string {
    if (c) {
        return a + b + c;
    }
    return a + b;
}

const result1 = concat('Hello', 'World', 'Stranger');  // 'HelloWorldStranger'
const result2 = concat('Hello', 'TypeScript');  // 'HelloTypeScript'

 

그렇기에 중요한건 오버로딩 시그니처를 생성할때 구체적인 시그니처를 가장 먼저 선언을 해줘야만 한다.

function greet(name: string): string;
function greet(age: number): number;
function greet(key: number): string;
function greet(value: any): any {
    if (typeof value === 'string') {
        return `Hello, ${value}!`;
    } else if (typeof value === 'number') {
        return value * 2;
    }
}

const greeting1 = greet('Alice');  // 'Hello, Alice!'
const greeting2 = greet(30);  // 60
const greeting3 = greet(125);  // 250

console.log(greeting1)
console.log(greeting2)
console.log(greeting3)

이렇게 매개변수에 number가 있어도 먼저 선언된 number를 반환하는 함수 시그니처를 먼저 인식해서 처리하게 된다.

또한 타입스크립트에서 오버로딩은 시그니처 정의만 할 수 있다는 것이다.

구현부는 하나만 작성해야하고 시그니처에서 다룰 수 있는 타입을 기준으로 작성해줘야만 한다.