안녕하세요?
오늘은 함수 오버로딩에 대해 알아보겠습니다.
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);
이제 실행해볼까요?
아주 잘 되고 있네요.
이상 타입스크립트의 함수 오버로딩에 대해 알아보았습니다.
'코딩 > Typescript' 카테고리의 다른 글
타입스크립트 튜플 - 타입스크립트 TypeScript 강좌 6편 (0) | 2021.11.02 |
---|---|
타입스크립트 옵셔널 체이닝(Optional Chaining) - 타입스크립트 Typescript 강좌 5편 (0) | 2021.10.31 |
타입스크립트 콜백함수 알아보기 - 타입스크립트 Typescript 강좌 3편 (0) | 2021.10.30 |
타입스크립트 함수 알아보기 - 타입스크립트 TypeScript 강좌 2편 (0) | 2021.10.23 |
타입스크립트(Typescript) 강좌 1편 (0) | 2021.10.23 |