C#, Java 등의 언어에서 재사용성이 높은 컴포넌트를 만들 때 자주 활용되는 '틀' 역할을 해주는 제네릭에 대해 알아보자.
타입스크립트에서 타입을 마치 함수의 파라미터처럼 사용하는 것을 의미한다.
제네릭을 사용하면 클래스나 함수의 목적 중 하나인 재사용성이 증가한다. 한가지 타입보다 여러가지 타입에서 동작하는 컴포넌트를 생성하는데 사용된다.
기존 타입 정의방식의 문제점이 무엇인지 파악하고, 비교해보면 제네릭이 왜 필요한지 쉽게 알 수 있다.
function logText(text: string) {
console.log(text);
return text;
}
function logNumber(num: number) {
console.log(num);
return num;
}
logText('hi');
logNumber(10);
타입 종류에 따라 각각 다른 함수들을 만들어 주었다.
단순히 타입이 다른 인자를 받기 위해서 로직이 같은 함수를 여러개 만든다는 것은 유지,보수 관점에서 좋지 않다.
function logText(text: string | number) {
console.log(text);
return text;
}
const str = logText('hi');
str.split(""); //오류 발생
logNumber(10);
문자타입 인자를 문자열 api 'split'에 사용했음에도 오류가 발생한다. 정확한 타입을 인지해야 하는데 숫자타입이 들어오는지 문자타입이 들어오는지 모르기 때문에(숫자타입이 들어오는 경우에는 'split'이 제공되지 않음) 이와 같은 문제가 발생하는 것이다.
function logText<T>(text: T): T {
console.log(text);
return text;
}
const str = logText<string>("hi");
str.split("");
logText<number>(10);
제네릭을 사용하여 앞의 문제점들을 해결할 수 있었다.
함수의 이름 바로 뒤에 <T>
라는 코드를 추가하고, 함수의 인자와 반환 값에 모두 T라는 타입을 추가한다. 이렇게 되면 함수를 호출할 때 넘긴 타입에 대해 타입스크립트가 추정할 수 있게 된다.
//인터페이스
interface dropdown {
value: string;
selected: boolean;
}
const obj: dropdown = { value: "abc", selected: false };
// 인터페이스 + 제네릭
interface dropdown<T> {
value: T;
selected: boolean;
}
const obj: dropdown<string> = { value: "abc", selected: false };
function logText<T>(text: T): T {
console.log(text.length); //Error
return text;
}
인자의 타입에 선언한 T는 아직 어떤 타입인지 구체적으로 정의하지않았기 때문에 length
코드에서 오류가 발생한다. 해당 타입을 정의하지 않고도 length속성정도는 허용하려면 아래와 같이 작성해야 한다.
interface LengthWise {
length: number;
}
function logText<T extends LengthWise>(text: T): T {
console.log(text.length);
return text;
}
length에 대해 동작하는 인자만 넘겨받을 수 있게된다.
반환값을 따로 작성하지 않아도 타입스크립트는 코드 내에서 타입을 추론할 수 있다.
function fetchItems(): string[] {
const fruit = ['apple', 'banana', 'cherry'];
return fruit;
}
let result = fetchItems();
console.log(result);
하지만 비동기적 코드를 실행한다면 프로미스 안에 들어오는 비동기 코드의 타입을 알 수가 없다. 프로미스는 기본적으로 제네릭을 이용하여 정의한다.
function fetchItems(): Promise<string[]> {
const items = ['a', 'b', 'c'];
return new Promise(resolve => {
resolve(items);
});
}
fetchItems();