제네릭이란 함수나 인터페이스, 타입 별칭, 클래스와 같은 다양한 타입을 이용해 객체를 동작하도록 만들어주는 타입스크립트의 기능이다.
function passThrough(value: any) {
return value;
}
const num = passThrough(10);
const str = passThrough("gisuk");
num.toUpperCase() // 런타임 에러
입력받은 값을 그대로 반환하는 함수가 있다고 가정하고 함수를 만들었다.
다양한 타입이 들어올수 있기 때문에 타입의 제한을 주지 않고 타입을 any로 주었다.
이 때 타입스크립트는 타입 검증을 하지 못하기 때문에 위 코드에 적힌 number타입에 toUpperCase 메서드를 사용하더라도 컴파일 에러를 뱉지 않게 되어, 치명적인 런타임 에러를 발생시킨다.
function passThrough(value: unknown) {
return value;
}
const num = passThrough(10);
const str = passThrough("gisuk");
num.toUpperCase() // 컴파일 에러
num.isNaN() // 컴파일 에러
if(typeof num === "number") {
num.isNaN()
}
잘못된 메서드를 방지하기 위해 unknown 타입을 사용할 경우, 타입에 따른 메서드를 사용할때 마다 타입 검증을 추가해 주는 귀찮은 작업을 해야한다.
function passThrough<T>(value: T):T {
return value;
}
const num = passThrough(10);
// number 타입
const str = passThrough("gisuk");
// string 타입
num.toUpperCase() // 컴파일 에러
num.isNaN()
하지만 제네릭을 사용하면 할당된 함수의 타입은 함수가 호출될때 결정된다. 함수에 number 타입의 값을 전달하면 매개변수에는 number 타입의 값이 저장되어 T는 number 타입으로 추론이 되고, 함수의 반환 타입또한 number 타입이 된다.
interface KeyPair<K, V> {
key: K;
value: V;
}
const keyPair: KeyPair<string, number> = {
key: "key",
value: 0,
};
제네릭으로 KeyPair 인터페이스를 선언한 뒤, 다음 객체의 타입을 KeyPair<string, number>로 정의 했다. 결과 key의 프로퍼티는 string 타입, value의 프로퍼티는 number 타입을 가진 객체 타입이 된다.