V-logue

[Typescript] 노마드코더 TypeScript로 블록체인 만들기 (3-1) 본문

발자취/Typescript

[Typescript] 노마드코더 TypeScript로 블록체인 만들기 (3-1)

보그 2022. 8. 12. 00:06

Overloading

 

TS에서는 같은 이름의 함수를 다중으로 정의할 수 있고, 각 함수는 서로 다른 타입을 가져야 한다.

이와 같이 같은 이름의 함수를 다중으로 정의하고, 변수의 타입을 달리 정의하는 것을 TS에서 function Overloading

함수 오버로딩이라고 한다. typescript에서 Overload Signature는 overload 함수를 위해서만 사용된다.

 

실제로 많은 오버로딩된 함수를 작성할 일은 많지 않을 것이다. 그 대신,
대부분의 시간을 다른 사람들이 만든 패키지나 라이브러리 같은 것을 사용할 텐데
이런 패키지나 라이브러리는 Overloading을 매우 많이 사용한다.
그래서 제대로 이해하기 위해서는 Overloading이 무엇인지 이해하고 넘어가야 한다. 앞서서 배운

type Add = (a: number, b:number) => number;

와 같은 것은 단축키로 call signature를 만드는 가장 빠른 지름길 이었다.
앞서서 배운 type Add는 조금 더 길게 작성할 수 도 있는데,

type Add = {
    ( a : number, b : number ) : number  
}

이런 방법이 존재하는 이유는 오버로딩 떄문이다. 오버로딩은 대체 무엇일까?
오버로딩은 함수가 여러개의 call signature를 가지고 있을 때 발생한다.


+++

 

잠깐, 문제가 발생했다. 내 vscode상으로 뜨는 메세지와 노마드 코더에서 강의한 메세지 내용이 다르다.

코드가 똑같기 때문에, typescript 공식 홈페이지에서 검증해보니 ts 공홈은 노마드코더랑 같게 결과가 나왔다.

type Add = {
    ( a : number, b : number ) : number
    ( a : number, b : string ) : number 
}

const add : Add = (a, b) => a + b

 

위와 같은 코드가 있다고 칠 때, 내 쪽에선 Arrow function add의 인수 a와 b의 값이 any로 나오지만

(parameter) a: any
'a' 매개 변수는 암시적으로 'any' 형식이지만, 사용량에서 더 나은 형식을 유추할 수 있습니다.ts(7044)

노마드 코더에선 아래와 같이 나온다.

아마도 내쪽에 문제가 좀 있어보이는데... 문제는 왜 결과가 달리 나오는지 알 길이 없다는 점...

 

++++

 

해결했다.

ts파일로만 만든다고 ts가 되는게 아니라, ts를 따로 설치 했어야 했는데 vscode에 설치하지 않아서 발생

 

먼저 package.json 파일부터 만든다.

 npm init -y

그리고 package.json파일에 typescript를 추가해준다.

 npm i typescript  // 1
 npm install -g typescript // 2

ts를 1번으로 설치하는 것이 아니라, 2번으로 설치해야 다음 단계로 진입이 된다.

그리고 타입 스크립트의 환경설정 파일을 설치해준다.

이 환경설정 파일을 설치하기 위해서 2번으로 설치하는 것이다.

'tsc' is not recognized as an internal or external command,
operable program or batch file.

만약 1번으로 설치하면 위 처럼 메세지가 나타나고 제대로 동작하지 않는다. 2번으로 설치했다면,

tsc --init

여기까지 잘 따라왔다면, 나와 같은 문제가 발생한 사람도

노마드 코더나 ts 공홈처럼 제대로 call signature의 overloading이 잘 동작 할 것이다.


다시 원점으로 돌아와서, 다시 강의를 설명하면,

 

보다시피 Add 함수가 2개의 call signature로 정의 됐고, TS에선 아무런 문제도 없다고 한다.
( a : number, b : number ) : number 형식으로 부를 수도 있고,
( a : number, b : string ) : number 형식으로도 부를 수 있다.
a + b에 에러가 발생하는데
'+' 연산자를 'number' 및 'string | number' 형식에 적용할 수 없습니다.ts(2365)라고
메세지가 나타난다. b가 string이 될 수도 number가 될 수도 있기 때문에 
string과 number를 더할 수 없다고 에러가 발생한 것이다. 그래서 확인을 거쳐야 한다.

const add : Add = (a, b) => {
    if(typeof b === "string") return a
    return a + b
}

만약 b의 타입이 string이면 a를 리턴하고, 그게 아니라면 a + b를 리턴한다.
이렇게 짜면 조건을 만족은 하는데, 매우 소수의 함수만 이런식으로 핸들링 할 수 있어서
매우 나쁜 예시다. 별로 큰 의미는 없지만 오버로딩의 핵심을 알 수는 있다.

 

그리고 next.js 얘기가 갑자기 나오는데,

만약 페이지를 바꾸고 싶다면 이런식으로 할 수 있다고 한다.

Router.push("/home")

push를 하는데, string으로 home을 주면 home 페이지로 이동시켜줄 것이다. 
또한 객체 형식으로 보내줄 수 있는데 예를들면, 객체 안에 path를 "/home"으로 설정하고
그리고 추가적으로 뭔가 더 넣어서 보내줄 수 있다.

Router.push({
    path : "/home",
    state : 1
});

이게 완벽한 오버로딩의 예시인데, 두 가지가 모두 잘 작동한다. 이게 next.js의 실제 예시인게
string으로도 보내고, object로도 보내기 때문이다. 이걸 오버로딩으로 잘 활용하려면

type Config = {
    path : string,
    state : object
}

type Push = {
    ( path : string ) : void
    ( config : Config ) : void
}

첫 번째 signature로 string을 받게해주고, void로 리턴시킨다. 참고로 void는 아무것도 리턴하지 않는다.
그리고 두 번째는 Config type을 또 만들어서 void를 리턴한다. 이제 push를 구현하면

const push : Push = ( config ) => {
    if(typeof config === "string") console.log(config)
    else {
        console.log(config.path, config.state)
    }
}

만약 config의 type이 string이라면 config를 리턴하는데, config는 void기 때문에 아무것도 리턴하지 않는다.
이게 나중에 볼 아주 좋은 예시가 될 것이다. 이런 방식은 패키지나 라이브러리를 디자인할 때 많이 사용한다.
config가 string이라면 마우스를 올려보면 파라미터가 string이라는 것을 알려준다.
하지만 else에선 이게 type Config객체라는 것을 파라미터로 알 수 있고 path와 state값을 가질 수 있는
Config타입을 


오버로딩의 효과에 대해 더 알아야 하는데 그것은 argument가 여러개 있을 때이다.
이제 다시 Add 로 돌아가면,

type Add = {
    (a: number, b: number, c: number): number;
    (a: number, b: number): number;
    };


이전에는 둘이 같은 파라미터를 가졌는데, 지금은 위쪽은 3개고 아래는 2개다.
call signature의 파라미터 개수가 다르기 때문에, 

const add : Add = (a, b, c) => {
    return a + b
}

add 함수가 동작하지 않는다. 
'(a: number, b: number, c: number) => number' 형식은 'Add' 형식에 할당할 수 없습니다.
라는 메세지가 나오게 되는데, 다른 파라미터를 가지기 때문에 발생하는 문제인데 
다른 파라미터를 가지게 되면 나머지 파타미터에도 타입을 지정해줘야 한다.
사실 a와 b를 부를 수도, a b c를 다 부를 수도 있는데 이건 옵션이다. 그렇기 때문에
c를 부를 수도 부르지 않을 수도 있다. c는 아마도 number일 거라고 가정하고, ?를 추가하면

const add : Add = (a, b, c?:number) => {
    return a + b
}

문제가 해결되는데 이 경우는 파라미터의 개수가 다른 경우이고, 그 의미는 개수다 차이나기 시작하는
파라미터 부터는 옵션이라는 소리가 된다. 그래서 추가적으로 타입을 지정하고, 선택사항임을 알린다.

const add : Add = (a, b, c?:number) => {
    if(c) return a
    return a + b
}

물론 이 경우는 흔히 볼 수 있는 케이스는 아니고, 아마도 첫번 째 케이스가 대부분일 것이다.
(String과 Config를.. 어쩌구 했던 위에서)

 

테스트를 해보면,

const add : Add = (a, b, c?:number) => {
    if(c) return a
    return a + b
}

add(1, 2);
add(3, 4, 5);

완벽하게 잘 작동한다.

Comments