이 글은 Chat GPT로 TypeScript를 공부하며 정리한 글입니다.
“아직 정하지 않은 타입을 위한 자리 표시자”
제네릭은 타입을 값처럼 전달할 수 있는 문법이다.
즉, 실행 시점이 아니라 호출 시점에 타입을 결정할 수 있다.
function identity<T>(value: T): T {
return value;
}
identity<string>("hi"); // "hi"
identity<number>(123); // 123
여기서 <T>는 타입 매개변수(Type Parameter) 이며
함수 호출부에서 타입이 자동으로 결정(inference)된다.
💡 핵심
T는 “타입을 위한 변수”이다. 값처럼 전달되지만 타입 공간에서만 존재한다.
제네릭 함수는 <T> 같은 타입 매개변수를 사용해
입력 타입에 따라 자동으로 반환 타입을 맞추는 함수다.
function wrapInArray<T>(value: T): T[] {
return [value];
}
wrapInArray(3); // number[]
wrapInArray("hello"); // string[]
장점
함수뿐 아니라 type alias나 interface에서도 제네릭을 정의할 수 있다.
type FormErrors<T> = Partial<Record<keyof T, string>>;
예시:
type UserErrors = FormErrors<{ name: string; age: number }>;
// {
// name?: string;
// age?: string;
// }
💡 제네릭 타입은 “타입을 만들어내는 타입 공장”이다.
타입 매개변수는 여러 개도 가능하다.
function pair<T, U>(a: T, b: U) {
return [a, b];
}
pair("hello", 123); // [string, number]
추가 팁:
T, U 같은 이름도 좋지만<Item, Result>처럼 의미가 드러나는 이름이 가독성에 더 좋다.extends를 사용하여 타입 매개변수에 조건을 걸 수 있다.
function logLength<T extends { length: number }>(x: T) {
console.log(x.length);
}
logLength([1, 2, 3]); // OK
logLength("hi"); // OK
logLength(42); // Error
여기서 extends는 상속이 아니라
“이 조건을 최소한으로 만족해야 한다”는 의미이다.
function getId<T extends { id: number }>(data: T): number {
return data.id;
}
getId({ id: 1, name: "foo" }); // OK
interface User {
id: number;
username: string;
}
getId({ id: 42, username: "chan" }); // OK
| 용어 | 설명 | 예시 |
|---|---|---|
| 제네릭 | 타입을 변수처럼 다루는 문법 | <T> |
| 제네릭 함수 | 타입을 인자로 받는 함수 | function fn<T>(x: T) |
| 제네릭 타입 | 제네릭 타입 alias / interface | type Box<T> = { value: T } |
| 제약(Constraint) | 타입 조건 제한 | <T extends { id: number }> |
제네릭은 기본값을 지정할 수 있다.
function getValue<T = string>(value?: T): T {
return (value ?? "default") as T;
}
getValue(); // T = string
getValue(42); // T = number
function toArray<T = number>(value: T): T[] {
return [value];
}
toArray(1); // number[]
toArray("hi"); // string[]
toArray(); // 오류: 인자가 없어서 T 추론 불가
getValue<T = string>(value?: T) 처럼 매개변수가 선택적이면,
인자를 넣지 않고 getValue()를 호출해도 된다.
이때 제네릭 T는 추론할 근거가 없으므로, 기본값인 string이 사용된다.
반대로 toArray<T = number>(value: T)처럼 매개변수가 필수라면,
toArray()는 제네릭 추론 이전에 “필수 인자가 빠졌다”는 이유로 에러가 난다.
즉, 이 에러는 T의 기본값이 적용되지 않아서가 아니라,
“필수 매개변수에 값을 전달하지 않았기 때문”이다.
제네릭 기본값은 함수 호출이 성립해야 적용된다.
기본값은 ‘추론 실패 시’ 적용된다.
즉, 추론 근거가 있는데 기본값이 우선되는 일은 없다.
React, TanStack Query, Zustand 등 많은 라이브러리 내부에서 다음과 같은 형태가 흔하다.
function createFetcher<TData = unknown, TError = Error>() {
return async function fetchData(): Promise<TData> {
// ...
throw new TError("Fetch failed");
};
}
unknownError| 개념 | 설명 | 예시 |
|---|---|---|
| 제네릭 추론 | 입력값으로 T 자동 결정 | identity("hi") → T = string |
| 기본 타입 지정 | <T = string> 기본값 제공 | getValue() → string |
| 제약(extends) | T에 구조적 조건 부여 | <T extends { id: number }> |