"제네릭은 아직 정하지 않은 타입을 위한 빈칸이 아니라, 입력과 출력이 같은 타입이라는 관계를 보존하는 장치야."
중복과 any 사이의 세 번째 길
배열의 첫 원소를 돌려주는 함수를 타입마다 따로 만들면 구현이 반복돼. 모든 배열을 any[]로 받으면 함수는 하나지만 반환 타입 정보가 사라져. 제네릭은 구현 하나를 유지하면서 호출마다 실제 원소 타입을 붙잡아 두 문제를 함께 푼다.
function first<T>(items: T[]): T에서 T는 호출 시점에 정해져. 숫자 배열을 넣으면 T는 number, 문자열 배열이면 string이야. 반환 타입에도 같은 T가 쓰였으므로 입력에서 얻은 정보가 출력까지 이어진다.
Array와 Promise도 같은 원리
Array<T>, Promise<T>, Map<K, V>는 특별한 내장 마법이 아니라 타입 매개변수를 가진 제네릭 타입이야. 빈칸에 어떤 타입을 넣느냐에 따라 같은 구조가 다른 값의 계약이 된다.
이 관점을 잡으면 라이브러리 타입이 덜 낯설어져. 꺾쇠괄호 안의 타입이 어느 위치에 나타나고 어떤 관계를 이어 주는지 추적하면 돼.
타입 슬롯 하나로 관계를 보존해
제네릭이 없다면 배열의 첫 원소를 얻기 위해 firstString, firstNumber, firstUser를 복제하거나 any를 써야 해. first<T>(arr: T[]): T의 T는 호출자가 채우는 슬롯이라 숫자 배열에서는 number, 문자열 배열에서는 string이 입력부터 반환까지 이어져. Array<T>도 같은 원리의 객체 타입이고 Array<string>은 string[]과 같아.
제네릭의 핵심은 재사용보다 관계 보존이야. 같은 T가 어디에서 들어와 어디로 흘러가는지 읽어.