숏컷 코드
function identity<T>(value: T): T {
return value;
}
const n = identity(1);
const s = identity("hello");관계 보존
제네릭에서 먼저 자주 보는 기본형은 아래입니다.
- 함수 제네릭:
function identity<T>(value: T): T - 제네릭 인터페이스:
interface Box<T> { value: T } - 제네릭 클래스:
class Queue<T> { ... } - 입력과 출력 관계 보존:
map<T, R>(...)
1) 입력과 출력의 관계를 그대로 보존하면 제네릭
identity<T>(value: T): T는 어떤 타입이 들어오든 같은 타입이 나간다는 계약을 유지합니다.
function first<T>(items: T[]): T | undefined {
return items[0];
}이 관계 보존이 핵심이고, 이 점이 없으면 any와 차이가 크게 줄어듭니다.
function firstAny(items: any[]): any {
return items[0];
}
function firstGeneric<T>(items: T[]): T | undefined {
return items[0];
}앞쪽 함수는 편하지만 결과 타입을 잃고, 뒤쪽 함수는 입력 원소 타입을 끝까지 보존합니다.
2) 함수뿐 아니라 타입과 인터페이스에도 제네릭을 붙인다
제네릭은 함수 문법만이 아니라 재사용 가능한 타입 구조를 만드는 기본 도구이기도 합니다.
interface ApiResponse<T> {
data: T;
status: number;
}
const userResponse: ApiResponse<{ id: string }> = {
data: { id: "u1" },
status: 200,
};즉 제네릭은 코드 재사용뿐 아니라 타입 재사용의 기본 재료입니다.
class Queue<T> {
private items: T[] = [];
push(value: T) {
this.items.push(value);
}
shift(): T | undefined {
return this.items.shift();
}
}3) any와의 차이는 타입 정보를 버리지 않는다는 점이다
any는 편하지만 타입 정보를 잃어버립니다.
제네릭은 같은 유연함처럼 보여도, 실제로는 "어떤 타입이 들어왔는지"를 끝까지 유지합니다.
4) 제네릭 매개변수는 기본적으로 아무것도 모른다
T는 아무 타입이나 될 수 있으므로, 내부에서 특정 속성이나 메서드를 쓰려면 제약이 필요합니다.
function logValue<T>(value: T) {
console.log(value);
}이 지점이 바로 다음 카드인 constraints와 keyof로 이어집니다.
5) 제네릭이 필요 없는 곳까지 억지로 붙이지 않는다
타입 관계를 보존할 이유가 없고, 구체 타입이 더 읽기 쉬우면 그냥 구체 타입을 쓰는 편이 낫습니다. 즉 제네릭은 만능 추상화가 아니라 관계 보존이 필요할 때 쓰는 도구입니다.
언제 제네릭이 필요할까
제네릭을 고를 때 볼 점
- 입력과 출력 타입 관계를 유지한다: 제네릭 함수
- 재사용 가능한 컨테이너 타입이다: 제네릭 타입/인터페이스
- 타입 정보를 버려도 된다: 정말 필요한 경우만
any - 아직 어떤 타입인지 모른다:
unknown - 특정 속성이 필요하다: 제약 추가 검토
주의할 점
제네릭을 쓴다고 자동으로 안전한 것은 아닙니다. 관계를 보존하지 않고 안쪽에서 any처럼 다루기 시작하면 이점이 사라집니다.
// ❌ 제네릭을 받지만 결과를 any처럼 망침
function badIdentity<T>(value: T) {
return (value as any).notReal;
}
// ✅ 입력과 출력 관계를 그대로 유지
function identity<T>(value: T): T {
return value;
}
// ❌ 관계 보존이 필요 없는데 과하게 제네릭
function logValue<T>(value: T): void {
console.log(value);
}
// ✅ 구체 타입이 더 읽기 쉬우면 그대로 둠
function logMessage(message: string): void {
console.log(message);
}참고 링크
1 sources