노트

제네릭

Generics

프런트엔드#ts · 연결된 개념 5개

쉽게 말하면

제네릭은 함수에 '들어올 타입은 나중에 정할게요'라는 빈칸을 두는 거예요. 같은 상자에 사과를 넣으면 사과 상자, 책을 넣으면 책 상자로 기억해서, 꺼낼 때도 뭐가 들었는지 알아요.

비유가 깨지는 곳 빈칸은 반환 타입이 아니에요. <string>은 T를 채울 뿐이고 반환 타입은 시그니처가 정해요. 뭐든 받는 any 상자와 달리 호출할 때 타입이 정해져 검사가 유지돼요.

제네릭(Generics)은 타입을 매개변수처럼 받아, 여러 타입에 같은 로직을 재사용하면서도 타입 정보를 잃지 않게 하는 기능이다.

function first<T>(arr: T[]): T { return arr[0] }  // <T>: 타입 변수 선언
first<string>(['a'])                               // <string>: T에 string 대입
first([1, 2])                                      // 보통은 추론으로 T = number
  • 타입 매개변수는 반환 타입이 아니다. <string>은 T에 값을 채울 뿐이고, 반환 타입은 시그니처가 정한다. reduce<Record<string, User[]>>(…)가 결과 타입처럼 보이는 건 reduce가 우연히 U를 반환하기 때문이다
  • any와의 차이: any[]를 받으면 결과도 any라 오류가 숨는다. 제네릭은 호출 시점에 타입이 정해져 검사가 유지된다
  • 제약(Generic Constraints, extends): <T extends { length: number }>처럼 받을 수 있는 범위를 좁힌다. <T, K extends keyof T>(obj: T, key: K): T[K]는 실제 있는 키만 받는다(keyof)
  • 인스턴스마다 타입을 고정하고 싶으면 제네릭 클래스(DataStorage<string>), 섞여도 되면 유니언을 쓴다
  • React의 useState<number>, useRef<HTMLInputElement>(null)도 제네릭이다

좋은 제네릭 쓰기 (Handbook 지침)

  • 타입 매개변수는 제약보다 실제 쓰이는 곳에 둔다: <T>(arr: T[])가 <T extends any[]>(arr: T)보다 추론이 정확하다
  • 가능한 한 적게 쓴다
  • 한 곳에만 나오는 타입 매개변수는 필요 없을 가능성이 높다

타입을 계산하는 쪽으로 나아가면 조건부 타입과 infer·유틸리티 타입가 된다. 과한 일반화는 YAGNI를 떠올린다.

출처: TypeScript Handbook - Generic Functions · Guidelines for Writing Good Generic Functions

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 기본형 집착

    전화번호·금액·우선순위 같은 도메인 개념을 끝까지 문자열이나 숫자로만 다루는 냄새. 같은 검증과 포맷 코드가 여기저기 반복되고, 문자열로 모든 걸 표현하는 "stringly typed" 코드가 된다.

  • 타입 추론과 타입 검사

    타입 추론(Type Inference)은 표기가 없어도 컴파일러가 값의 타입을 정하는 단계이고, 타입 검사(Type Checking)는 정해진(추론됐거나 명시된) 타입을 코드가 지키는지 확인하는 단계다. 보통 추론이 먼저 일어나고, 그 결과를 기준으로 검사한다.

  • 함수 오버로드와 시그니처

    함수 오버로드(Function Overloads)는 구현 하나 위에 호출 방식별 시그니처를 여러 개 선언해, 인자 조합에 따라 정확한 반환 타입이 추론되게 하는 TypeScript 기능이다.

  • type과 interface

    type(타입 별칭, Type Alias)과 interface는 둘 다 타입에 이름을 붙이는 방법이다. 객체 모양을 정의할 때는 대부분 바꿔 써도 되지만, 확장 방식과 표현 범위가 다르다.

  • any와 unknown

    any는 그 값에 대한 타입 검사를 포기하는 타입이고, unknown은 타입을 아직 모르니 확인하기 전에는 쓰지 못하게 하는 타입이다. 둘 다 아무 값이나 받을 수 있지만, 꺼내 쓸 때가 다르다.

보기 옵션