제네릭(Generic)은 코드 재사용성을 높이고 타입 안정성을 보장하는 기능이다. 제네릭 사용으로 함수나 클래스를 작성할 때, 사용될 데이터 타입을 미리 지정하지 않고, 이후에 함수나 클래스를 호출할 때 인자로 전달된 데이터의 타입에 따라 자동으로 타입을 추론하게 된다.
function printLog(text) {
return text;
}
위 함수는 파라미터로 text
를 받고 반환 값으로 text
를 리턴하고 있다. 이를 제네릭 없이 구현해보면 아래와 같다
function printLog(text: string): string {
return text;
}
printLog('hello'); // 정상
printLog(123); // 에러
첫번째 함수는 printLog
함수에 특정 타입을 작성한 코드다. 명시된 타입인 string
타입 외에 다른 타입이 들어오면 컴파일 에러가 난다.
이를 해결하기 위해 중복으로 함수를 선언하는 방법이 있다.
function printLog(text: string): string {
return text;
}
function printLogNumber(text: number): number {
return text;
}
printLog('hello'); // 정상
printLogNumber(123); // 정상
여기서 |
연산자를 이용해 유니온 타입으로 선언할 수 있다.
function printLog(text: string | number) {
return text;
}
printLog('hello'); // 정상
printLogNumber(123); // 정상
any
타입을 사용한 코드는 어떤 타입이든 받을 수 있지만 실제 함수가 반환할 때 어떤 타입인지 추론할 수 없게 된다.function printLog(text: any): any { return text; }
이 때 제네릭을 사용할 필요가 생긴다.
function printLog<T>(text: T): T {
return text;
}
printLog
함수에 T
라는 타입 변수를 추가해 유저가 준 파라미터의 타입을 캡처하고 이 정보를 나중에 사용할 수 있게 한다. 여기서 T
를 반환 타입으로 다시 사용한다.
interface Item<T> {
name: T;
stock: number;
selected: boolean;
}
Item
인터페이스를 사용해 만든 객체는 name
값으로 어떤 타입인지 작성해 준다면 재사용 가능하다.
const obj: Item<string> = {
name: "T-shirts",
stock: 2,
selected: false
};
const obj: Item<number> = {
name: 2044512,
stock: 2,
selected: false
};
이처럼 여러 개의 객체를 만들어 재사용 가능하다.
TypeScript에서 팩토리를 생성할 때 생성자 함수로 클래스 타입을 참조해야한다.
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
제네릭 함수를 만들 때 컴파일러가 함수 본문에 제네릭 타입화된 매개변수를 쓰도록 강요한다.
function printLog<T>(text: T): T {
console.log(text.length);
return text;
}
이처럼 console.log(text.length);
를 작성할 경우 컴파일 에러가 난다. 개발자가 string
타입이 아닌 number
타입을 보낼 수도 있기 때문에, T
에는 .length
가 있다는 것을 추론할 수 없다.
이때 제네릭에 타입을 줘 유연하게 함수의 타입을 정의해 줄 수 있다.
function printLog<T>(text: T[]): T[] {
console.log(text.length);
return text;
}
// or
function printLog<T>(text: Array<T>): Array<T> {
console.log(text.length);
return text;
}
T
라는 변수 타입을 받고, 인자 값으로는 배열 형태의 T
를 받는 코드다.
이 경우 제네릭 타입이 배열이기 때문에 .length
를 허용한다.