"제네릭 함수는 어떤 타입이 들어와도 된다는 말보다, 들어온 타입이 다른 자리에도 같은 모습으로 나타난다는 말을 해."
하나의 타입 매개변수
head<T>(items: T[]): T | undefined는 배열 원소 타입을 T로 붙잡고 반환값에 그대로 사용해. 빈 배열 가능성 때문에 undefined도 함께 말해. 숫자 배열 호출에서는 number 또는 undefined, User 배열에서는 User 또는 undefined가 된다.
이런 함수는 타입마다 구현을 복사하지 않아도 되고 any로 정보를 버리지도 않아. 함수 몸은 한 번 쓰되 호출마다 구체 계약이 만들어지는 셈이야.
여러 타입 매개변수로 변환 관계를 나타내
map<T, U>(items: T[], fn: (item: T) => U): U[]는 입력 원소 T가 콜백을 거쳐 U로 바뀌고 결과 배열도 U가 된다는 관계를 적어. T와 U는 서로 다를 수 있고, 컴파일러는 배열과 콜백을 함께 보며 둘을 추론해.
타입 매개변수가 많아지면 T, U 같은 짧은 이름보다 Input, Result, Key처럼 역할을 드러내는 이름이 나을 수 있어. 짧은 범위에서는 관례가 읽기 좋고, 긴 공개 타입에서는 뜻이 읽기 좋아야 해.
추론 방향을 설계해
제네릭 함수의 사용감은 어느 인자에서 타입을 추론할 수 있는지에 달려. 호출자가 먼저 제공하는 값에서 T를 알아내고 뒤의 콜백 매개변수로 문맥을 흘려보내면 자연스러워. 반대로 반환값에서만 T를 정해야 하면 호출자가 타입 인자를 직접 적어야 할 수 있다.
head<T>는 배열 원소와 반환 타입을 연결하고, map<T, U>는 입력 T를 콜백의 출력 U와 연결해 U 배열을 만들어. 대부분의 호출에서는 인자에서 타입이 추론되지만, 추론할 정보가 없거나 의도와 다를 때는 identity<string>('x')처럼 명시할 수 있어.