타입스크립트의 제네릭(Generic)은 재사용 가능한 컴포넌트 또는 함수를 작성할 때 타입 안정성을 확보하기 위해 사용되는 기능이다. 제네릭을 사용하면 타입을 매개 변수화하여 다양한 타입을 처리할 수 있는 유연한 코드를 작성할 수 있다.
제네릭이란 타입을 마치 함수의 파라미터처럼 사용하는 것
제네릭은 함수, 클래스, 인터페이스 등에 적용할 수 있다. 타입 매개변수는 일반적으로 <T>
와 같은 형식으로 작성되며, 함수와 클래스 내부에서 해당 매개변수를 사용할 수 있다. 예를 들어 다음은 제네릭을 사용한 함수의 예시다.
function identity<T>(arg: T): T {
return arg;
}
위의 identity
함수는 어떤 타입의 매개변수 arg
를 받아서 동일한 타입의 값을 반환하는 단순한 예시다. 제네릭을 사용하여 arg
의 타입이 함수 호출 시점에 결정되도록 하고 있다. 예를 들어 다음과 같이 사용할 수 있다.
let result = identity<string>("Hello"); // result 타입은 string
위의 예제에서는 identity
함수를 호출할 때 <string>
을 명시하여 타입 매ㅐㄱ변수를 string
으로 설정했다. 이렇게 하면 result
의 타입이 string
으로 결정된다.
제네릭은 타입 매개변수를 여러 개 사용할 수도 있으며, 제네릭 타입에도 제약을 추가할 수 있다. 또한, 타입 매개변수에 기본 타입을 지정하거나 타입 매개변수 간의 관계를 정의하는 등 다양한 활용이 가능하다.
개인적으로 이렇게만 봐서 이해가 잘 안가서 다른 문서에서 예시를 더 찾아봤다.
function getText(text) {
return text;
}
위 함수는 text
라는 파라미터에 값을 넘겨 받아 text
를 반환해준다. h1
, 10
, true
등 어떤 값이 들어가더라도 그대로 반환한다.
getText("hi"); // "hi"
getText(10); // 10
getText(true); // true
이 관점에서 제네릭을 한번 살펴보자면
function getText<T>(text: T): T {
return text;
}
위 함수는 제네릭 기본 문법이 적용된 형태다. 이제 함수를 호출할 때 아래와 같이 함수 안에서 사용할 타입을 넘겨줄 수 있다.
getText<string>("hi");
getText<string>(10);
getText<string>(true);
위 코드 중 getText<string>("hi")
를 호출 했을 때 함수에서 제네릭이 어떻게 동작하는지 살펴보자.
function getText<string>(text: T): T {
return text;
}
먼저 위 함수에서 제네릭 타입이 <string>
이 되는 이유는 getText()
함수를 호출할 때 제네릭(함수에서 사용할 타입) 값으로 string
을 넘겼기 때문이다.
getText<string>();
그리고 나서 함수의 인자로 h1
라는 값을 아래와 같이 넘기게 되면
getText<string>("hi");
getText
함수는 아래와 같이 타입을 정의한 것과 같다.
function getText<string>(text: string): string{
return text;
}
위 함수는 입력 값의 타입이 string
이면서 반환 값 타입도 string
이어야 한다.
또 다른 예제를 살펴보자.
function logText(text: string): string {
return text;
}
위 코드는 인자를 하나 넘겨 받아 반환해주는 함수다. 여기서 이 함수의 인자와 반환 값은 모두 string
으로 지정되어 있지만 만약 여러 가지 타입을 허용하고 싶다면 아래와 같이 any
를 사용할 수 있다.
이러한 문제점을 해결할 수 있는 것이 제네릭이다.
function logText<T>(text: T): T {
return text;
}
먼저 함수의 이름 바로 뒤에 <T>
라는 코드를 추가했다. 그리고 함수의 인자와 반환 값에 모두 T
라는 타입을 추가한다. 이렇게 되면 함수를 호출할 때 넘긴 타입에 대한 타입스크립트가 추정할 수 있게 된다. 따라서, 함수의 입력 값에 대한 타입과 출력 값에 대한 타입이 동일한지 검증할 수 있게 된다.
그리고 이렇게 선언한 함수는 아래와 같이 2가지 방법으로 호출할 수 있다.
// #1
const text = logText<string>("Hello Generic");
// #2
const text = logText("Hello Generic");
보통 두 번째 방법이 코드도 더 짧고 가독성이 좋기 때문에 흔하게 사용된다. 그렇지만 만약 복잡한 코드에서 두 번쨰 코드로 타입 추정이 되지 않는다면 첫 번째 방법을 사용하면 된다.
참고
타입스크립트 핸드북