________________________________________________________________________
TypeScript 에서 함수를 다뤄보자 -2-
< overloading 오버로딩 >
< overloading 오버로딩 - 실제로 우리가 만나게 될 예시 >
오버로딩이 중요한 점.
실제로 개발에 들어갔을 때 그리 많은 ‘오버로딩 된 함수’ 를 작성 하지는 않을거야.
대신, 난 아마 대부분에 시간을 다른 사람들이 만든 외부 라이브러리를 사용하는데에 투자하게 될 거야.
그러한 패키지나 라이브러리들은 오버로딩을 매우 많이 사용해.
즉, 오버로딩에 대한 개념이 잡혀 있어야지, 다른 사람의 코드들도 잘 읽어낼 수 있다는 뜻.
1.
_________________________
< 오버로딩 >
이전 시간에 봤던 call signatures 를 다시 살펴보자.
——
type Add = (a:number, b:number) => number // signatures
const add : Add = (a, b) => a + b
——
=> 우리가 타입스크립트에게 add 라는 함수가 어떻게 호출되는 건지를 설명해 주는 부분이지.
위와 같은 방식은 단축키와도 같아. 가장 빠르고 간단하게 signatures 를 만드는 방법이지.
아래처럼 만들 수도 있어.
——
type Add2 = {
(a:number, b:number) : number
}
——
=> 이런 방법이 존재하는 이유는 오버로딩 때문.
오버로딩은 함수가 여러 개의 call signatures 를 가지고 있을 때 발생시켜.
그냥 여러 개가 아니라 서로 다른 여러 개의 call signatures 를 가졌을 때.
다음과 같이 signatures 타입이 여러 개의 signatures 를 가진다고 가정해보자. 조금 바보같은 예시 이긴 한데, 어쨌든.
——
type Add2 = {
(a:number, b:number) : number
(a:number, b:string) : number
}
const add2 : Add2 = (a, b) => a + b // 에러 발생. Operator '+' cannot be applied to types 'number' and 'string | number'
——
=> 에러가 발생해. 왜?
넘버인 a 와 문자일수도, 숫자일수도 있는 b 를 합칠 수는 없다고 설명하고 있어.
즉 두 가지의 signatures 가 곂치면서 b 는 number 일 수도, string 일 수도 있게 된거야.
따라서 아래와 같이 먼저 확인을 해줘야 해.
——
type Add2 = {
(a:number, b:number) : number
(a:number, b:string) : number
}
const add2 : Add2 = (a, b) => {
if (typeof b === "string") {
return a
}
return a + b
}
——
=> 이렇게 if 로 분기를 해주면 두 가지 signatures 를 모두 만족시킬 수는 있어.
— 추가 —
——
const add2 : Add2 = (a, b) => { // 에러 발생
if (typeof b === "string") {
return a
}
if (typeof b === "number") {
return a + b
}
}
——
=> 이처럼 작성했을 경우,
add2 에 값을 할당할 수 없다면서 에러가 떠. 왜? 함수 안에서 모든 경우에서 return 을 하는 것은 아니다라면서.
(parameter) b: string | number >> b의 시그니처 설명인데, 그냥 이렇게 명시해주는 것과 시그니처로 명시해주는 건 다른가봐.
(b: string | number) 이렇게 타입을 명시해 줬을 때는 if 문 두 개로 오류가 안났는데, 이 경우에는 에러가 발생.
따라서
——
const add2 : Add2 = (a, b) => {
if (typeof b === "string") {
return a
}
if (typeof b === "number") {
return a + b
}
return a
}
——
이런 식으로 모든 상황에서 어떻게든 return 이 되도록 고쳐줘야 해.
허나 위의 예시는 그리 좋은 예시가 아니야.
왜? 위처럼 signatures 를 작성할 수 있는 건 극히 제한된 소수의 경우에서만 가능해서.
따라서 사실상 의미 없는 예시이지만, 오버로드의 예시를 보여줄 수 있지.
다시 말하자면, 오버로딩은 여러 call signatures 가 있는 함수에서 발생하는 현상.
이 경우 add2 라는 함수가 Add2 라는, 다수의 signatures 를 지닌 타입을 취하게 되었다는 것.
2.
_________________________
< 오버로딩 - 자주 등장하는 예시 >
이제 실제로 우리가 겪을 만한 오버로딩 예시를 들어보자.
——
type Config = {
path: string,
state: object
}
type Push = {
(path: string) : void,
(config: Config): void
}
const push: Push = (config) => {
if (typeof config === "string") {
console.log(config);
} else {
console.log(config.path)
}
}
——
=> 해당 xxx.push() 라는 건 react 에서 해당 주소의 페이지를 랜더해줄 때 사용하는 메소드를 예시로 든 거야.
node.js 에서 res.render() 를 예시로 들어볼까?
res.render(‘/home’) 이런 경우가 있는 가 하면 res.render(‘/home’, {data: data}) 이렇게 data 를 함께 보내주는 경우도 있었지.
즉, render() 라는 함수에 매개변수로 path 만 들어가냐, 아니면 추가적으로 {} 도 함께 들어가냐에 따라 다른 시그니쳐가 적용되었던 거야.
즉 오버로딩이 발생했다는 거지.
위의 예시에서 (config) => {} 에서 매개변수 이름 config 는 중요하지 않아.
중요한 건, 저 매개변수에 들어있는 값이 string 이면 (path: string) : void 에 해당하고,
그 외이면 (config: Config): void 에 해당하게 된다는 거지.
이 경우
push(“/home“) 이냐
push({ path:”/home”, state: {data: result} }) 이냐
이거지.
매개변수로 들어온 값이 string 이냐, 아니면 Config 타입의 객체냐에 따라서 다르게 오버로딩을 해준 다는 것.
이것이 핵심.
*** 시그니쳐일 경우 선언되는 변수에 type 이 붙게 되고,
그냥 함수의 리턴 타입을 명시하고 십다면 함수의 () 뒤에 type 이 붙는다… 맞나?
위의 예시에서는 각각의 call signatures 들이 하나의 매개변수 만을 받고 있는 경우.
그렇다면, 각각의 시그니쳐들이 각기 다른 갯수의 매개변수를 받고 있을 때는 어떻게 명시 하면 좋을까?
——
type Add3 = {
(a:number, b:number) : number,
(a:number, b:number, c:number) : number
}
const add3 : Add3 = (a, b, c?:number) => {
if (c) return a + b + c;
return a + b;
}
add3(1, 2);
add3(1, 2, 3);
——
=> (a, b, c?:number) 이렇게 옵션을 주는거지. 옵셔널 타입 마냥.
그리고 내부에서는 c 가 있냐 없냐에 따라 return 을 달리 해주면 오류는 발생하지 않아.
사실 파라미터의 갯수가 다른 경우에 대응하여 시그니쳐를 여러 개 작성한다는 경우는 많이 발생하지 않아.
이것 보다는 이전 예시인 path, object 쪽이 우리가 더 많이 만나게 될 예시.
'내일배움캠프_개발일지 > TypeScript 연습' 카테고리의 다른 글
TypeScript 연습 <1> -7- (0) | 2023.01.30 |
---|---|
TypeScript 연습 <1> -6- (0) | 2023.01.27 |
TypeScript 연습 <1> -4- (1) | 2023.01.25 |
TypeScript 연습 <1> -3- (0) | 2023.01.25 |
TypeScript 연습 <1> -2- (1) | 2023.01.20 |