[ TypeScript ] Generic 기초 개념

💡 Generic이란?

대개 코딩하면서, api를 참조할 때, 종종 다음과 같은 생소한 문법을 보았을 것이다.

function func<T1, T2>(param1: T1, param2: T2) {
	...
}

여기서 <T1, T2>가 Typescript의 강력한 Generic System이다.
쉽게 말하자면, 바로 타입에 변수를 전달하는 것이다!
좀 더 말로 쉽게 설명하자면,

T1과 T2라는 변수를 줄 건데, 이 변수는 추후, 우리가 나중에 다양한 타입으로 작성할 수 있어!

라고 말할 수 있겠다.
그렇다면 나를 비롯한 초보자는, 이런 생각이 들지도 모른다.

그렇다면, 굳이 제네릭이 any랑 다른 게 뭐야!

⭐타입 검사

이를 좀 더 찾아보았더니, 같게 쓸 수도 있고 다르게 쓸 수도 있다.
가령, 다음을 살펴보자.

다음은 같은 결과를 가져온다.

function example1<T1, T2>(a: T1, b: T2) {
    return [a,b]
}
function example2(a: any, b: any) {
    return [a, b]
}
console.log(example1('test', 1)) // ['test', 1]
console.log(example2('test', 2)) // ['test', 2]

그러나 다음은, 다른 결과를 가져온다.

function example1<T1, T2>(a: T1, b: T2):T2[] {
    return [a,b]
} // Error! (T1과 T2가 같은 유형인지 판단이 불가하므로)
function example2(a: any, b: any) {
    return [a, b]
}
console.log(example1('test', 1)) // Error!
console.log(example2('test', 2)) //

여기서 차이를 느낄 수 있다.
any는 아무 유형이든 흔쾌히 받아들인다.
그러나 generic은 두 타입이 다를 가능성을 염두한다.
결과적으로, 제네릭은 유효한 타입 검사인 것이다!
만약 위 오류를 해결하려면 다음과 같이 작성해야 할 것이다.

function example1<T1, T2>(a: T1, b: T2):(T1|T2)[] {
    return [a, b]
}

공식문서에는 위에 관한 설명이 다음과 같이 표기되어 있다.

any를 쓰는 것은 함수의 arg가 어떤 타입이든 받을 수 있다는 점에서 제네릭이지만, 실제로 함수가 반환할 때 어떤 타입인지에 대한 정보는 잃게 됩니다. 만약 number 타입을 넘긴다고 해도 any 타입이 반환된다는 정보만 얻을 뿐입니다.

즉, 제네릭은 any처럼 보이지만, 자신의 타입을 '지켜낸다'는 표현이 적절할 지도 모르겠다.

⭐재사용성

또한, 재사용성 역시 뛰어나다는 점 역시 제네릭의 큰 매력이다. 다음 예시를 살펴보자.

다음은 특정 배열의 인덱스를 찾아서, 원하는 값으로 업데이트해주는 함수 구현이다.

function updateArr<T>(arr:T[], target: T, replaceTo: T):T[] {
    const idx = arr.indexOf(target);
    arr[idx] = replaceTo;
    return arr;
}
console.log(updateArr([1,2,3,4], 4, 999)) // [1,2,3,999]

이를 실제로 구현하려면, 제네릭이 아닌 경우는 any를 사용해야 한다.
다음을 보자.

function updateArr2<T>(arr:any, target: any, replaceTo: any):any[] {
    const idx = arr.indexOf(target);
    arr[idx] = replaceTo;
    return arr;
}
console.log(updateArr([1,2,3,4], 4, 999))// [1,2,3,999]

잘 작동하지 않아서 좋지 않나요?!라는 생각이 들 정도로, 잘 된다.
그러나, 다음을 명령하면 결과는 어떨까?

console.log(updateArr([1,2,3,4], '4', 999)) // Error checked!
console.log(updateArr2([1,2,3,4], '4', 999)) //[ 1, 2, 3, 4, '-1': 999 ]

결과적으로 전자는 위의 케이스에 대하여 타입 검사를 제대로 했지만, 후자는 타입 검사를 통해 오류를 처리하지 못하고 있다.

💬 정리하며

타입스크립트의 궁극적인 목적은, 유효한 타입인지를 검사하여 사전에 오류를 빠르게 체크하기 위한 것이다.
이러한 측면에서 볼 때, 제네릭이 특정 타입을 지켜내고 싶을 때,

  • any보다, 타입 검사를 더욱 우수하게 할 수 있음을 알 수 있으며,
  • 특정 타입을 추가로 입력하지 않고 들어오는 값에 따라 설정되기에 재사용성이 높다 :)

좋은 웹페이지 즐겨찾기