글 작성자: 망고좋아
반응형

🎯 타입스크립트 함수 오버로딩 : Function Overloading

  • 동일한 이름에 매개 변수만 다른 여러 버전의 함수를 만드는 것을 함수의 오버로딩이라고 한다.
  • 파라미터의 형태가 다양한 여러 케이스에 대응하는 같은 이름을 가진 함수를 만드는 것
  • 함수의 다형성(다양한 형태)을 지원하는 것
  • function 키워드로만 함수 오버로딩을 할 수 있으며 arrow function으로는 오버로딩을 할 수 없다.

 

📝 Function Overloading는 언제 사용할 수 있을까?

const addZero = (num: number) => (num > 9 ? "" : "0") + num;

function formatDate(date: Date, format = "yyyyMMdd"): string {
    const yyyy = date.getFullYear().toString();
    const MM = addZero(date.getMonth() + 1);
    const dd = addZero(date.getDate());
    return format.replace("yyyy", yyyy).replace("MM", MM).replace("dd", dd);
}

function formatDateString(dateStr: string, format = "yyyyMMdd"): string {
    const date = new Date(dateStr);
    const yyyy = date.getFullYear().toString();
    const MM = addZero(date.getMonth() + 1);
    const dd = addZero(date.getDate());
    return format.replace("yyyy", yyyy).replace("MM", MM).replace("dd", dd);
}

function formatDateTime(datetime: number, format = "yyyyMMdd"): string {
    const date = new Date(datetime);
    const yyyy = date.getFullYear().toString();
    const MM = addZero(date.getMonth() + 1);
    const dd = addZero(date.getDate());
    return format.replace("yyyy", yyyy).replace("MM", MM).replace("dd", dd);
}
  • 파라미터만 달라지고, 비슷한 로직이 반복되는 경우에 사용할 수 있다.
  • 코드의 중복을 줄이고 재사용성을 높이려면 어떻게 해야 할까? ⇒ Function Overloading

 

📝 함수 오버로딩을 위해 해야 할 것 2가지

  1. 선언 : 함수가 어떤 파라미터 타입들을 다룰 것인지 선언
  2. 구현 : 각 파라미터 타입에 대응하는 구체적인 코드를 작성(ex. string일 때는 ~~, number일 때 ~~)

 

📝 함수 오버로딩 선언할 시 주의할 점

  • 함수의 이름은 같아야 한다.
  • 매개변수의 순서는 서로 같아야 한다. (매개변수가 추가되는 것은 괜찮다! 순서는 꼭 지켜줘야 된다.)
// 문제 없는 함수 오버로딩 선언
class User {
    constructor(private id: string) {}
    setId(id: string): string;
    setId(id: number): string;
}


// Error
class User {
    constructor(private id: string) {}
    // 선언 시에 에러는 나지 않지만 오버로딩 함수 정의 시에 에러
    setId(id: string): void;
    setId(id: string): number; // 반환 타입 다름
    setId(radix: number, id: number): void; // 인수 순서 다름
}

 

📝 함수 오버로딩 구현

📕 매개변수의 개수가 같을 때

class User {
    constructor(private id: string) {}

    setId(id: string): void;
    setId(id: number): void;

        // 유니온 타입으로 선언 & 타입 가드
    setId(id: string | number): void {
        this.id = typeof id === "number" ? id.toString() : id; // 타입 가드
    }
}

 

📕 매개변수의 개수가 다를 때

class User {
    constructor(private id: string) {}

    setId(id: string): void;
    setId(id: number, radix: number): void;

        // 유니온 타입으로 선언 & 옵셔널 & 타입 가드
    setId(id: string | number, radix?: number): void { // radix는 number or undefined
        this.id = typeof id === "number" ? id.toString(radix) : id;
    }
}

 

📝 함수 오버로딩 예제

📕 리팩토링 전

const addZero = (num: number) => (num > 9 ? "" : "0") + num;

function formatDate(date: Date, format = "yyyyMMdd"): string {
    const yyyy = date.getFullYear().toString();
    const MM = addZero(date.getMonth() + 1);
    const dd = addZero(date.getDate());
    return format.replace("yyyy", yyyy).replace("MM", MM).replace("dd", dd);
}

function formatDateString(dateStr: string, format = "yyyyMMdd"): string {
    const date = new Date(dateStr);
    const yyyy = date.getFullYear().toString();
    const MM = addZero(date.getMonth() + 1);
    const dd = addZero(date.getDate());
    return format.replace("yyyy", yyyy).replace("MM", MM).replace("dd", dd);
}

function formatDateTime(datetime: number, format = "yyyyMMdd"): string {
    const date = new Date(datetime);
    const yyyy = date.getFullYear().toString();
    const MM = addZero(date.getMonth() + 1);
    const dd = addZero(date.getDate());
    return format.replace("yyyy", yyyy).replace("MM", MM).replace("dd", dd);
}

 

📕 함수 오버로딩으로 리팩토링

const addZero = (num: number) => (num > 9 ? '' : "0") + num;
function formatDate(date: Date, format = "yyyyMMdd"): string;
function formatDate(date: number, format = "yyyyMMdd"): string;
function formatDate(date: string, format = "yyyyMMdd"): string;

// 위 3개 함수를 함수 오버로딩 처리, date는 유니온 타입사용
function formatDate(date: string | Date | number, format = "yyyyMMdd"): string {
    const dateToFormat = new Date(date);

    // … dateToFormat validation … 만약 empty나 1, 0이 들어왔을 때 validation 처리해주기

    const yyyy = dateToFormat.getFullYear().toString();
    const MM = addZero(dateToFormat.getMonth() + 1);
    const dd = addZero(dateToFormat.getDate());

    return format.replace("yyyy", yyyy).replace("MM", MM).replace("dd", dd);
}

 

📕 또 다른 예

class User {
  // 생성자 안에서 접근 제한자 키워드가 있다면 파라미터로 넘기는 동시에 할당(this.id = id)해준다.
  constructor(public id?: string) {}

  // 선언
  setId(id: string): void;
  setId(id: number): void;

  // 구현
  setId(id: string | number): void {
    // 구현 내용
    this.id = typeof id === 'number' ? id.toString() : id 
  }
}

const elice = new User();

elice.setId(12345);
console.log(elice.id, typeof elice.id);

elice.setId("123");
console.log(elice.id, typeof elice.id);
  • constructor(public id?: string) {} 는 생성자 안에서 접근 제한자 키워드가 있다면 파라미터로 넘기는 동시에 할당(this.id = id)해준다.

 

📝 제네릭과의 차이점

  • 타입을 추론할 수 있는 시점과 용도의 범위
  타입 추론 시점 용도의 범위
제네릭 사용되는 시점 제네릭 타입, 인터페이스, 클래스, 함수, 메서드 등
함수 오버로딩 타입을 선언하는 시점 함수, 메서드
  • 함수 파라미터에 들어갈 타입을 알고 있고, 파라미터 타입만 달라지고 함수의 로직이 반복된다면 함수 오버로딩 사용
  • 어떤 타입이 올지 모르겠고, 타입을 다양한 용도에서 사용하고 싶다면 제네릭 사용
반응형