Adventure Time - Finn 3
본문 바로가기
프론트

TypeScript 제네릭(Generic)

by hyun9_9 2026. 4. 11.

타입을 미리 고정하지 않고, 사용하는 시점에 결정하는 제네릭의 기본부터 제약 조건까지 정리합니다.


1. 제네릭이 필요한 이유

동일한 함수를 다양한 타입의 배열에 사용하고 싶다고 가정해봅시다.

// number 배열, string 배열 모두 길이를 구하고 싶다면?
// → 함수 오버로드? 유니온 타입?
// → 타입이 계속 늘어나면 그때마다 추가해야 한다

오버로드나 유니온으로도 해결할 수 있지만, 타입이 늘어날 때마다 코드를 수정해야 합니다. 제네릭을 사용하면 선언 시에는 타입을 비워두고, 호출 시에 타입을 결정할 수 있습니다.


2. 제네릭 함수

<T>를 타입 파라미터라고 합니다. 함수를 호출할 때 전달하는 타입에 따라 T가 결정됩니다.

function getSize<T>(arr: T[]): number {
  return arr.length;
}

const arr1 = [1, 2, 3];
getSize<number>(arr1);  // 3 — T가 number로 결정

const arr2 = ['1', '2', '3'];
getSize(arr2);           // 3 — T가 string으로 자동 추론

💡 <number>처럼 타입을 직접 기입할 수도 있고, 생략하면 전달된 매개변수를 보고 TypeScript가 자동으로 추론합니다. 특정 타입을 명확히 강조하고 싶을 때만 직접 기입하면 됩니다.


3. 제네릭 인터페이스

인터페이스에서도 제네릭을 사용할 수 있습니다. 특정 프로퍼티의 타입이 상황에 따라 달라질 때 유용합니다.

interface Mobile<T> {
  name: string;
  price: number;
  option: T;          // 어떤 타입이든 올 수 있음
}

사용 예시

// option이 객체인 경우
const m1: Mobile<object> = {
  name: "s21",
  price: 1000,
  option: {
    color: "red",
    coupon: false,
  }
}

// option이 문자열인 경우
const m2: Mobile<string> = {
  name: "s21",
  price: 1000,
  option: "red"
}

객체 구조가 정해져 있다면 Mobile<{ color: string; coupon: boolean }>처럼 구체적으로 지정할 수도 있습니다.


4. 제네릭 제약 조건 (extends)

제네릭은 어떤 타입이든 받을 수 있다는 장점이 있지만, 그래서 특정 프로퍼티가 있다고 보장할 수 없는 문제가 생깁니다.

문제 상황

interface User {
  name: string;
  age: number;
}
interface Car {
  name: string;
  color: string;
}
interface Book {
  price: number;
}

function showName<T>(data: T): string {
  return data.name;  // ❌ Error — T에 name 속성이 있다고 보장할 수 없음
}

User나 Car에는 name이 있지만, 제네릭 T는 어떤 타입이든 될 수 있으므로 TypeScript는 name에 접근하는 것을 허용하지 않습니다.

extends로 해결

T extends { name: string }을 사용하면 "T는 최소한 name: string을 가진 타입이다"라는 제약을 걸 수 있습니다.

function showName<T extends { name: string }>(data: T): string {
  return data.name;  // ✅ OK — name이 있다고 보장됨
}

const user: User = { name: "a", age: 10 };
const car: Car = { name: "bmw", color: "red" };
const book: Book = { price: 3000 };

showName(user);  // ✅ OK — name 있음
showName(car);   // ✅ OK — name 있음
showName(book);  // ❌ Error — Book에는 name이 없음

💡 extends는 "이 타입을 확장한 것만 허용한다"는 의미입니다. 제네릭의 유연함을 유지하면서도, 필요한 프로퍼티가 반드시 존재하도록 안전하게 제한할 수 있습니다.


한눈에 보기

기능 문법 설명

제네릭 함수 function fn<T>(arg: T) 호출 시 타입 결정
타입 명시 호출 fn<number>(arg) 타입을 직접 지정
타입 추론 호출 fn(arg) 매개변수로 자동 추론
제네릭 인터페이스 interface Mobile<T> 프로퍼티 타입을 유동적으로
제약 조건 T extends { name: string } T가 가져야 할 최소 조건