재사용 가능한 컴포넌트를 작성하는 것은 소프트웨어 엔지니어링에서의 중요한 부분입니다. 이러한 재사용 가능한 컴포넌트를 생성하는 주요 도구 중 하나가 바로 제네릭입니다. 제네릭을 사용해서 함수를 생성하는 경우와 그렇지 않은 경우, 어떤 점이 다른지 알아 봅시다.
🤯 제네릭을 사용하지 않는 경우
1. string 타입의 인자만 받을 수 있는 함수
function getText(text: string): string {
return text;
}
getText 함수는 string 타입의 text만 받을 수 있습니다. 그런데 만약 getText 함수에 string 타입을 제외한 타입의 text를 넘긴다면 어떻게 될까요?
function getText(text: string): string {
return text;
}
// error: Argument of type 'number' is not assignable to parameter of type 'string'.
getText(123);
number 타입의 인자를 string 타입의 파라미터에 할당할 수 없다는 에러가 나네요. 만일 getText 함수가 string 타입 이외의 타입 변수를 받지 않는다면 위 코드처럼 작성해도 문제는 없습니다. 그러나 함수가 한 가지 타입 변수만 받을지 불분명하고, 재사용 가능한 함수를 생성해야 한다면 위 같은 코드는 좋지 않습니다.
2. any 타입으로 인자를 받는 함수
function getText(arg: text): any {
return text;
}
만약 여러 가지 타입을 허용하고 싶다면 위와 같이 any 사용할 수 있습니다. any를 쓰면 함수의 동작에 문제가 생기진 않지만, 함수의 인자로 어떤 타입이 들어갔고 어떤 값이 반환되는지는 알 수 없습니다. 왜냐하면 any라는 타입은 타입 검사를 하지 않기 때문입니다.
이러한 문제점을 해결할 수 있는 것이 제네릭입니다.
🤗 제네릭 사용하기
1. 제네릭 기본 문법
제네릭은 단일 타입이 아닌 다양한 타입에서 작동하는 컴포넌트를 작성할 수 있습니다.
function getText<T>(text: T): T {
return text;
}
함수의 이름 바로 뒤에 <T>라는 코드를 추가하고 함수의 인자와 반환 값에도 T 라는 타입을 추가합니다. 이렇게 작성하면 함수를 호출할 때 넘긴 타입에 대해 타입스크립트가 추정할 수 있게 됩니다. 따라서, 합수의 입력 값에 대한 타입과 출력 값에 대한 타입이 동일한지 검증할 수 있게 됩니다.
그리고 이렇게 선언한 함수는 아래와 같이 2가지 방법으로 호출할 수 있습니다.
// #1
const text = getText<string>("Hello Generic");
// #2
const text = getText("Hello Generic");
보통 두 번째 방법이 코드도 더 짧고 가독성이 좋기 때문에 흔히 사용되는데요. 만약 복잡한 코드에서 두 번째 코드로 타입 추정이 되지 않는다면 첫 번째 방법을 사용하면 됩니다.
2. 제네릭 타입 변수
function getText<T>(text: T): T {
return text;
}
만약 여기서 함수의 인자로 받은 값의 length를 확인하고 싶다면 어떻게 해야 할까요?
function getText<T>(text: T): T {
console.log(text.length); // error: T doesn't have .length
return text;
}
위 코드를 변환하려고 하면 컴파일러에서 에러를 발생시킵니다. 어디에도 text 인자에 length가 있다는 단서는 없기 때문이죠. 제네릭은 함수의 인자와 반환 값 타입에 마치 any를 지정한 것과 같은 동작을 하기 때문에, 인자에 number 타입을 넘기더라도 에러가 발생하지는 않습니다. 이러한 특성 때문에 text에 문자열이나 배열이 들어와도 아직은 컴파일러 입장에서 length를 허용할 수 없습니다.
이런 경우에는 아래와 같이 제네릭에 타입을 줄 수 있습니다.
function getText<T>(text: T[]): T[] {
console.log(text.length); // 3
return text;
}
getText([1, 2, 3]);
제네릭 함수 코드는 T라는 변수 타입을 받고, 인자 값으로는 배열 형태의 T를 받습니다. 이제는 getText 함수에 [1, 2, 3] 처럼 숫자로 이루어진 배열을 넘기면 length를 참조할 수 있게 됩니다.
참고 자료
'TypeScript' 카테고리의 다른 글
[TypeScript] type vs interface (0) | 2021.12.22 |
---|