V-logue

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

발자취/Typescript

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

보그 2022. 8. 12. 01:26

다형성(Polymorphism)

 

다형성이란 무엇일까?
그리스어로 Poly는 many, several, much, multi를 의미한다.

여러개의 각을 가지고 있고, 여러개의 면을 가지고 있다.
morpho는 fomr(형태), structure(구조)란 뜻을 가지고 있다. 
이것들을 조합하면 many(poly) + structure(morphos)이고, 결국 polymorphism이란
여러가지 다른 구조를 의미한다. 여러가지 다른 모양 etc...

 

이전에는 ts는 2~3개의 파라미터를 가질 수 있다고 했다.
또는 ts에서 함수는 string이나 object를 첫 번째 파라미터로 가질 수 있다고도 했다.
그렇기 때문에 이전에 이미 다형성에 대해서 체험해본 것과 같다. 

 

이번 3-2강의에서는 배열을 받고, 그 배열의 요소를 하나씩 print해주는 함수를 만들 것이다.
숫자로 이루어진 배열을 받고 그 숫자를 하나씩 출력한다.
타입과 상관없이 그저 하나씩 출력할 예정,

 

type SuperPrint = {
    ( arr : number[] ) : void
    ( arr : boolean[] ) : void
}

const superPrint : SuperPrint = (arr) => {
    arr.forEach(el => console.log(el))
}

일단 배열을 받아서 그 배열을 forEach문으로 한바퀴돌려서 console로 찍어볼 것이다.

superPrint([1, 2, 3, 4])
superPrint([ true, false, true, false])
superPrint(["a","b","c","d"])

위와 같은 상황일 때 superPrint(["a","b","c","d"])를 위해서

type SuperPrint = {
    ( arr : number[] ) : void
    ( arr : boolean[] ) : void
    ( arr : string[] ) : void
}

SuperPrint에 string[]을 추가하는게 과연 정답일까? 정답은 맞지만 정답이 아니다.
아마 동작은 할 것이다. 하지만 답은 아니다. 여기서 다형성을 잘 활용하는 길이 있다.

 

concrete type이 존재한다. concrete type이란 우리가 지금 껏 봐왔던 void, unknown같은 타입을 말한다.
여기서 ts에 generic 타입을 받을 거라고 알려줄 건데, generic이란 type의 placeholder같은 것을 말한다.

superPrint([1, 2, true, "4"])

만약 위 코드를 동작시키고 싶다면 어떻게 해야 할까? 
일단 SuperPrint 상 존재하는 배열 말고도 어떤 any타입이든 동작시키고 싶다면

type SuperPrint = {
    ( arr : number[] ) : void
    ( arr : boolean[] ) : void
    ( arr : string[] ) : void
    ( arr : (number | boolean | string)[] ) : void
}

하지만 이것도 건설적이진 못하다. generic을 사용해보자,
call signature을 작성할 때, 확실히 들어올 타입을 모른다면 generic을 사용하게 되는데
단지 타입이라는 것만 알지 concrete type중 하나 겠지만, 정확히 무엇인지는 모른다.
혹은, concrete type을 모를 떄도 generic을 사용한다. 
generic을 사용하고 싶다면, 먼저 ts에 argument가 generic을 사용하겠다고 알려야 한다. <>

type SuperPrint = {
    <TypePlaceholder>( arr: TypePlaceholder[] ) : void
}

generic을 사용하게 되면, 모든 문제가 해결되는데 TS는 각 함수가 동작할 때마다
그 함수의 type을 generic이 사용됐다면 입력된 값을 통해 추론된 값을 사용하고 보여준다.
아주 아주 편리한 기능이다. 

superPrint([1, 2, 3, 4])
const superPrint: <number>(arr: number[]) => void
superPrint([ true, false, true, false])
const superPrint: <boolean>(arr: boolean[]) => void
superPrint(["a","b","c","d"])
const superPrint: <string>(arr: string[]) => void
superPrint([1, 2, true, "4"])
const superPrint: <string | number | boolean>(arr: (string | number | boolean)[]) => void

이제 superPrint가 arr을 받고, 배열의 첫 번째 요소를 return하는 함수라고 치고
그리고 void를 TypePlaceholder로 바꿔준다. 왜냐면 void가 아닌 arr[0]을 return하는 함수니까
함수가 TypePlaceholder의 배열을 받고, TypePlaceholder 중 하나[0]를 return하게 됐다라고 바꿔보면

type SuperPrint = {
    <TypePlaceholder>( arr: TypePlaceholder[] ) : TypePlaceholder
}

const superPrint : SuperPrint = (arr) => arr[0]

const a = superPrint([1, 2, 3, 4]) // const a: number
const b = superPrint([ true, false, true, false]) // const b: boolean
const c = superPrint(["a","b","c","d"]) // const c: string
const d = superPrint([1, 2, true, "4"]) // const d: string | number | boolean

이게 바로 다형성(polymorphism)이다. SuperPrint type은 number배열일 수도 있고,
string + number나 boolean + number일 수도 있다.

사실 generic은 대충 입력해도 된다.

type SuperPrint = {
    <xy>( arr: xy[] ) : xy
}

generic 선언을 통해 ts가 타입을 추론하게 하고, 그 배열의 값 중 하나를 리턴하게 만들었다.
사실 이게 generic을 사용하는 가장 일반적인 방법은 아니다.

사실 generic을 사용하면 any랑 무슨 차이가 있는 건가 싶었는데, 댓글을 보니 3-3에 나온다고 한다.


일반적인 방법과 any와의 차이점은 다음 강의에... 3-3에서..

Comments