
10강의 주제는 Generic... TS의 첫번째 난관...
제네릭은 C#, java 등의 언어에서 재사용성이 높은 컴포넌트를 만들 때 자주 활용되는 특징을 가지고 있다.
특히 한가지 타입보다 여러 가지 타입에서 동작하는 컴포넌트를 생성하는데 사용된다.
Generic은 꺽쇠안에 T가 들어가 있는 공포스러운 모양이라고 캡틴 판교님이 강의에서 설명하셨다. 근데 진짜 좀 공포스럽다...ㅋ
타입을 마치 함수의 파라미터의 개념으로 받게 되는 게 제네릭이다
첫번째는 함수에서의 제네릭 적용모습이다.
logText라는 함수를 호출할 때 이 안에 넘길 텍스트 타입을 호출할 때 넘겨줄 것이라고 정의할 수 있다.

이후에 함수를 호출할 때 인자를 넘겨주지 않으면 다음과 같은 에러가 발생한다.

그래서 여기에 문자열을 넣으면 다음과 같은 스펙을 가지게 된다.

호출할 때 hi라는 문자열을 넘겨 주었기 때문에 기본적으로 파라미터의 타입은 하이라고 하는 문자열이 된다. 따라서 문자열을 들고가서 그대로 한바퀴 돈 다음에 반환하는 것까지도 문자열이 된다.
제네릭한 느낌을 주고싶은 경우, 명시적으로 로그텍스트에 넘기는 타입을 지정할 수 있다.

기존 타입 정의 방식은 다음처럼 각 타입에 맞는 함수를 모두 만들어야 했다.

위의 코드를 해결하기 위해 비슷한 구조의 함수를 또 만들어야하는 번거로움...

이렇게 여러 개를 만들어서 사용해도 되지만 사실 코드의 유지보수 관점에서는 안좋다.
단순히 타입을 다르게 받기 위해서 중복되는 코드들을 계속해서 생산해 나가는 것들은 코드의 가독성과 유지보수 측면에서 안 좋기 때문이다. 이러한 중복코드를 해결할 수 있는 첫번재 방법으론 유니온을 이용하는 방법이 있다.
설명전에 먼저 유니온타입에 대한 복습!
유니온 방식: 여러 개의 타입이 올 수 있다고 지정
아래처럼 유니온을 사용하면 string과 number 두개를 모두 받을 수 있다.

스펙도 text는 string 또는 number 타입이라고 잘 지정된다.

그러면 다음과 같이 들어가는 타입에 대한 문제점은 사라진다.

이렇게만 하면 유니온 타입을 사용하면 모든게 해결된것처럼 보인다. 하지만 이 방식에는 문제점이 있다.
먼저 타입을 받은 상황에서 접근을 해보면 이건 string과 number의 교집합, 그러니까 스트링과 넘버가
공통으로 접근할 수 있는 속성과 API에 대해서만 프리뷰를 제공한다.
이처럼 text가 string과 number 둘 다 되는 문제점 발생한다.


이는 변수에 함수의 반환값을 할당해도 마찬가지다.
문자를 넣었음에도 불구하고 string과 number가 동시에 되기 때문에

위와같이 split을 사용할 수 없음. 왜냐하면 string | number라는 타입에는 해당 속성이 없기 때문이다.
split은 정확하게 타입이 string일 때 사용이 가능하다.
이처럼 유니온의 사용은 인풋에 대한 해결은 됐지만 반환값에 대한 해결이 되지 않아서 여전히 문제점이 생기게 된다.
그렇다면 제네릭이 위의 두 문제점을 어떻게 해결할 수 있는지 확인해보자.
첫번째 밑줄은 어떤 타입을 받을 건지 먼저 정의(꺾쇠)하는 것으로 함수에 나는 이 Generic을 쓸 거야라는 뜻이다.

다음 밑줄은 그랬을 때 함수를 정의할 때 받은 타입을 인자로 넘기겠다라는 것으로 즉 함수의 파라미터의 타입으로 쓰겠다라고 정의한다는 뜻이다.

마지막 밑줄은 그 값을 반환값의 타입으로 쓴다는 뜻이다.

함수의 호출시점에서 string이라는 타입을 쓰겠다고 정의하고 싶으면 다음처럼 사용하면 된다.

이렇게하면 함수의 return값이 string으로 설정된 것을 확인할 수 있다.

또한 string으로 제네릭을 설정하였고 해당 타입을 text의 타입으로 정의하여 text도 string이라는 타입을 받아와서 split이 사용가능하다. 이 점이 바로 유니온을 사용한 해결법과의 가장 큰 차이점이다.

split을 해서 이게 문자열임을 TS도 알고 개발자도 알고 타입을 틀어지지않게 잘 구성해 나갈 수 있도록 도와주는 것이 generic의 장점이다.
동일한 함수에서 다음처럼 제네릭을 boolean으로 변경해도 해당 타입이 제네릭으로 잘 전달된것을 확인할 수 있다.
이런 식의 타입 정의에 대한 이점을 제네릭이 확실히 가져간다.

제네릭
- 타입을 각각 정의하는 것이 아니라 함수를 정의할 때 타입을 비워 놓은 상태에서 그 타입에 뭐가 들어갈지는 호출 시점에 정의하는 것
- 그리고 그 타입을 추론해서 최종 반환 값까지 붙일 수 잇는 것
실전 예제의 미션: email과 numberOfProducts를 다 사용 가능하도록 타입을 지정하기
먼저 기본코드에 다음과 같이 value와 selected의 타입들을 지정해준다.

다음으로는 내부에 적어준 타입들을 interface로 빼서 타입을 지정한다.

다음은 각각 다른 타입의 데이터들을 인자로 받는 함수에 대해서 인자에 대한 타입을 지정해야한다.
이때 단순히 유니온타입 사용하면 타입 정의에 대한 코드가 늘어난다…
value라는 속성 하나가 다르다고 각각의 interface가 모두 필요한... 중복코드라는 영 찜찜하고 개발자로서 어떻게든 없애고 싶은 코드가 생긴다.


실전예제를 제네릭으로 해결하기전에 먼제 interface에 Generic을 적용하는 방법을 알아보자.
먼저 기본적인 interface의 구조는 다음과 같다.

근데 만약 value의 값이 언제든 바뀔 수 있다면 드랍다운에 꺾쇠를 열고 T를 적용해서 제네릭을 다음과 같이 설정한다.

제네릭이 아닐경우에는 다음처럼 해당타입에 일치하는 값만 호출하는 함수의 인자로 넘길 수 있다.

만약 제네릭으로 정의한 인터페이스를 적용할 때 꺾쇠를 열어서 호출시점에 타입을 넘겨주지 않으면

이렇게 제네릭 타입이 하나가 필요하다는 에러가 발생한다. 인터페이스 안에다 특정 속성에 대한 타입을 정해야한다는 뜻이다.
그래서 이렇게 number와 string을 꺾쇠를 열어 DropDown이라는 interface의 value의 타입으로 넘겨주게 되면 다음처럼 올바르게 매칭될 경우에는 에러가 발생하지 않는 것을 확인할 수 있다.


이건 내가 스스로 해본건데 저렇게 위치를 통해서 제네릭을 여러개로 설정할 수 있다. 전달받은 T라는 타입은 value의 타입으로 적용하고 V라는 타입은 selected의 타입으로 지정한다고 interface를 만든 뒤
밑에서 호출할때 꺾쇠를 열어 T에 해당하는 타입과 V에 해당하는 타입을 넘겨주면 된다.
그러면 넘겨진 타입으로 이루어진 인터페이스의 스펙에 맞추어 잘 할당된 변수는 에러가 발생하지 않는다.
obj2의 경우에는 value에는 number, selected에는 string을 넘기기로 했는데 selected에 boolean 값을 할당해서 타입 스펙에 맞지 않아 에러가 발생한 것이다.

그리하여 예제를 적용하면 다음과 같은 흐름으로 해결할 수 있다.



이렇게 제네릭을 활용해서 인터페이스로 코드를 줄일 수 있다.


반환 타입을 안 정해도 괜찮지만 최대한 명시적으로 선언해주는 것이 코드 가독성의 측면이나 기본적인 제네릭의 문법을 따라가기에 좀 더 옳다고 볼 수 있다.

사실 저 밑에 hi를 넣어서 이게 문자열이다라는 건 개발자만 아는것이지 타입스크립트 입장에서는 T에 뭐가 들어올지 전혀 알 수 없음 그래서 저렇게 T라는 타입이 뭐인지 알 수 없는 것

그래서 이렇게 배열로 변경해서 length를 사용할 수 있게 하는 방법이 있다.

근데 사실 이게 무슨 말인지 완벽하게 이해가지않아서 나는 이렇게 사용햇다.

미리 인터페이스에 타입을 정의하는 방법도 있다.

제네릭으로 받은 타입은 LengthType의 하위 타입일 것이기 때문에 저렇게 적용이 가능하다.
원래는 이렇게 모든 값을 호출시점에서 넘겨주는 것이 가능하다.

하지만 keyof라는 것을 써서 extends keyof + interface 조합으로 사용하게 되면 다음고 같이 에러가 발생한다.

이후에 해당 인자를 비우고 Ctrl + spacerBar를 사용해서 자동완성으로 확인하면 해당 인터페이스의 속성들로 가이드가 세워지는 것을 확인할 수 있다.

이런식으로 함수에 들어가는 인자는 쇼핑 아이템이라고 이미 정의 되어있는 타입에서 키값들만 들어갈 수 있다고 키오브라는 예약어를 사용해서 타입을 제약하는 것으로 여기에서는 name, price,stock만 가능하다.

