코딩/Typescript

타입스크립트 함수 오버로딩 - 타입스크립트 강좌 4편

드리프트 2021. 10. 30. 23:29
728x170

 

안녕하세요?

 

오늘은 함수 오버로딩에 대해 알아보겠습니다.

 

C++이나, Java 같은 고급 언어에서는 함수 오버로딩을 지원하는데요.

 

함수 오버로딩이란 함수 이름은 같지만 함수의 인자가 다를 경우를 지원해주는 방식입니다.

 

그럼, 타입스크립트에서 함수 오버로딩하는 방법에 대해 알아보겠습니다.

 

예제 파일을 만들어 볼까요?

 

// parseCoordinate.ts

interface Coordinate {
    x: number;
    y: number;
}

function parseCoordinateFromObject(obj: Coordinate): Coordinate {
    return {
        ...obj,
    }
}

function parseCoordinateFromNumbers(x: number, y: number): Coordinate {
    return {
        x,
        y,
    }
}

 

좌표(coordinate)를 관리하는 함수가 필요하다고 할 때 보통 좌표를 x, y로 관리하는데요.

 

타입스크립트에서는 interface를 써서 위 코드처럼 Coordinate란 인터페이스를 만들면 편합니다.

 

그럼 Coordinate를 리턴하는 parseCoordinate란 함수를 만들어 볼까요?

 

위 코드를 보시면 함수 두 개가 있습니다.

 

먼저, parseCoordinateFromObject란 함수는 Coordinate 타입의 객체를 받아서 Coordinate란 인터페이스를 리턴하는 함수고요.

 

두 번째, parseCoordinateFromNumbers란 함수는 x, y의 number 타입을 인수로 받아서 Coordinate란 인터페이스를 리턴하는 함수입니다.

 

두 함수의 목적은 Coordinate 타입을 리턴하는 건데요.

 

C++, Java에서는 이럴 경우 함수 오버로딩을 이용합니다.

 

즉, 함수 이름 한 개만으로 여러 타입을 해결할 수 있는 거죠.

 

타입스크립에서는 어떻게 하는지 알아볼까요?

 

function parseCoordinate(obj: Coordinate): Coordinate;
function parseCoordinate(x: number, y: number): Coordinate;

먼저, 함수 오버로딩(Overloading)할 함수 원형을 만듭니다.

 

위 코드처럼 한 개는 객체를 받는 함수, 두 번째는 숫자 두 개를 받는 함수입니다.

 

둘 다 Coordinate 객체를 리턴하죠.

 

함수 오버로딩할 함수 원형을 만든다음에는 실제 구현하는 함수를 만들어야 합니다.

 

function parseCoordinate(obj: Coordinate): Coordinate;
function parseCoordinate(x: number, y: number): Coordinate;
function parseCoordinate(arg1: unknown, arg2: unknown): Coordinate {
    let coord: Coordinate = {
        x: 0,
        y: 0,
    }

    return coord;
}

 

위 코드처럼 함수 오버로딩할 함수 원형을 적고, 그다음에 구현할 함수를 작성했는데요.

 

이때 구현한 함수에서 unknown이라는 타입을 볼 수 있는데요.

 

unknown은 타입스크립트에서 기본적으로 any 타입이지만 사용할 때는 다른 타입으로 캐스팅해야 하는 타입입니다.

 

타입이 어떤 건지 모르나 꼭 캐스팅하라는 의미죠.

 

위 코드를 작성하고 나니까 VS Code의 인텔리 센스가 에러를 보여주는데요.

 

오버로드 시그니처가 호환되지 않는다고 합니다.

 

왜일까요?

 

바로 첫 번째 함수 원형은 객체 하나를 받고, 두 번째 함수 원형을 숫자 두 개를 받습니다.

 

그런데 함수 오버로딩을 구현하는 우리의 실제 함수에서는 unknown 두 개를 받고 있죠.

 

즉, 함수 인자로 받는 인수의 개수가 틀렸습니다.

 

이럴 때 어떻게 할까요?

 

바로 타입스크립트의 옵셔널(Optional)을 쓰면 됩니다.

 

arg2 인자는 있을 수도 있고 없을 수도 있으니까 arg2뒤에 ?를 붙이면 됩니다.

 

function parseCoordinate(obj: Coordinate): Coordinate;
function parseCoordinate(x: number, y: number): Coordinate;
function parseCoordinate(arg1: unknown, arg2?: unknown): Coordinate {
    let coord: Coordinate = {
        x: 0,
        y: 0,
    }

    return coord;
}

 

arg2? 라고 하니까 VS Code의 인텔리 센스가 에러를 안 보여주네요.

 

이제, 실제 parseCoordinate 함수의 로직을 구현해야겠죠.

 

위 함수는 디폴트 좌표만을 리턴하고 있으니까요?

 

어떻게 할까요? 바로 자바스크립트의 typeof 함수를 이용해서 함수 인수가 객체일 경우와 아닐 경우를 나눠서 처리하면 됩니다.

 

function parseCoordinate(obj: Coordinate): Coordinate;
function parseCoordinate(x: number, y: number): Coordinate;
function parseCoordinate(arg1: unknown, arg2?: unknown): Coordinate {
    let coord: Coordinate = {
        x: 0,
        y: 0,
    }

    if (typeof arg1 === 'object') {

    } else {

    }

    return coord;
}

 

중간에 typeof arg1 === 'object'라는 if문이 보입니다.

 

그런데 왜 typeof arg1 === Coordinate라고 쓰지 않았을까요?

 

그건 바로 타입스크립트는 컴파일 타임에서의 에러 체킹을 하고 실제는 이것도 자바스크립트이기 때문에 런타임에서 체킹을 해줘야 합니다.

 

런타임에서 체킹 할 때는 'object' 이기 때문이죠.

 

그럼 마저 코드를 작성해 볼까요?

 

if (typeof arg1 === 'object') {
        coord = {
            ...arg1
        }
    } else {

    }

얼핏 보기에는 코드가 아무 이상이 없다고 생각할 수 있으나,

 

 

인텔리 센스는 위 그림과 같이 에러를 나타내고 있죠.

 

무슨 이유일까요?

 

바로 ...arg1 이라고 그냥 적으면 coord의 타입은 아무것도 아닌 게 되어 버립니다.

 

arg1 인자의 타입은 위에서 나온 unknown 타입인데요.

 

unknown 타입은 반드시 캐스팅해야 합니다.

 

그럼 어떻게 해야 할까요?

 

바로 타입스크립트의 as 키워드를 쓰면 됩니다.

 

if (typeof arg1 === 'object') {
        coord = {
            ...(arg1 as Coordinate)
        }
    } else {

    }

즉, arg1 이 처음에는 unknown 타입이었지만 as 키워드를 써서 실제 구현 시 Coordinate 타입이 된 겁니다.

 

인텔리 센스도 아무 불만을 제기하지 않네요.

 

else 일 때의 코드인데요.

 

    if (typeof arg1 === 'object') {
        coord = {
            ...(arg1 as Coordinate)
        }
    } else {
        coord = {
            x: arg1,
            y: arg2,
        }
    }

 

역시나 인텔리 센스는 에러가 있다고 하네요.

 

 

arg1, arg2 가 각각 unknown 이기 때문에 number 형식에 할당할 수 없다고 합니다.

 

역시 이 경우에도 unknown 타입을 as 키워드로 타입을 명확히 해야 합니다.

 

if (typeof arg1 === 'object') {
        coord = {
            ...(arg1 as Coordinate)
        }
    } else {
        coord = {
            x: arg1 as number,
            y: arg2 as number,
        }
    }

 

arg1 as number라고 하니까 에러가 없어졌네요.

 

이제 테스트를 위한 콘솔 로그를 해볼까요?

 

인텔리 센스가 아래 그림처럼 우리의 함수 오버로딩한 parseCoordinate 함수의 타입을 보여주고 있습니다.

 

두 가지의 경우가 있다고 친절히 알려주고 있네요.

 

 

1번째는 객체를 받는 경우, 두 번째는 2개의 숫자를 받는 경우로 잘 나오고 있습니다.

 

실행 결과를 볼까요?

 

아주 잘 나오고 있습니다.

 

이상 타입스크립트 상의 함수 오버로딩에 대해 알아봤는데요.

 

여기서 더 깊숙이 들어가 볼까요?

 

만약 다음과 같은 함수 오버로딩을 만들고 싶으면 어떻게 해야 할까요?

 

console.log(parseCoordinate("x:111,y:222"));

 

위 코드는 parseCoordinate 함수가 string (문자열)을 인수로 받는 경우입니다.

 

우리가 함수 오버로딩할 때 string(문자열)의 경우에 대해서는 코드를 작성하지 않았는데요.

 

이 경우의 함수 오버로딩도 한번 해볼까요?

 

먼저, 함수 오버로딩할 원형을 위에다 적어야 합니다.

 

function parseCoordinate(obj: Coordinate): Coordinate;
function parseCoordinate(x: number, y: number): Coordinate;
function parseCoordinate(str: string): Coordinate;

인수를 str이라는 string을 받는 함수 원형을 만들었습니다.

 

그럼 실제 이 경우의 루틴을 만들어야겠죠.

 

if (typeof arg1 === 'object') {
        coord = {
            ...(arg1 as Coordinate)
        }
    } else if (typeof arg1 === 'string') {

    } else {
        coord = {
            x: arg1 as number,
            y: arg2 as number,
        }
    }

 

위와 같은 코드로 arg1의 타입이 string 일 경우의 if문을 추가했습니다.

 

그리고 인수로 받은 문자열의 특징을 발견해야 하는데요.

 

잘 보시면 ,(콤마)로 나눌 수 있습니다.

 

그리고 다시 :(콜론)으로 x, y를 구분 지을 수가 있네요.

 

코드를 작성해 볼까요?

 

else if (typeof arg1 === 'string') {
        (arg1 as string).split(",").forEach((str) => {
            const [key, value] = str.split(":");
            coord[key] = parseInt(value, 10);
        });
    }

 

arg1 as string이라고 해야 하는 거 유의 바랍니다.

 

그리고 split(", ") 함수를 썼고 그다음 forEach함수로 다시 split(":") 함수를 썼습니다.

 

그런데, 인텔리 센스가 또 뭐라고 불평을 하는데요.

 

바로 다음과 같이 Coordinate 타입의 인덱스에 string을 사용할 수 없다고 하는데요.

 

그건 바로 Coordinate의 객체에서 키 밸류 관계에서 키는 x 아니면 y이기 때문입니다.

 

이 같은 경우에는 다음과 같이 as 키워드로 작성하면 됩니다.

 

아주 강력한 as 키워드네요.

 

 coord[key as 'x' | 'y'] = parseInt(value, 10);

 

이제 실행해볼까요?

 

 

아주 잘 되고 있네요.

 

이상 타입스크립트의 함수 오버로딩에 대해 알아보았습니다.

 

 

그리드형