[TYSCRIPT] 제네릭 제약조건(extends) 사용법 — 타입 안정성을 높이는 핵심 기법

제네릭(Generic)은 유연한 타입 설계를 가능하게 하지만, 아무 타입이나 허용하면 런타임 오류가 발생할 위험도 함께 증가합니다. 이 문제를 해결하는 방법이 제네릭 제약조건(extends)이며, “T는 반드시 특정 조건을 만족해야 한다”는 룰을 부여할 수 있습니다.

즉, 제네릭 제약조건을 올바르게 사용하면 유연성과 타입 안정성을 동시에 확보할 수 있습니다.

 

제네릭 제약조건의 기본 개념

function logLength<T extends { length: number }>(value: T) {
  console.log(value.length);
}

이 함수는 T가 어떤 타입이든 허용하지만, 반드시 length 속성을 가진 타입이어야 합니다. 즉, extends는 T에 “조건을 걸기 위한” 장치입니다.

 

객체 형태를 만족해야 하는 제네릭

function getValue<T extends object>(obj: T) {
  return obj;
}

getValue({ name: "Alice" }); // ✔
getValue(123); // number는 object 아님

객체만 받도록 제한하고 싶을 때 유용한 패턴입니다.

 

유니온 타입을 이용한 제약조건

type Status = "loading" | "success" | "error";

function setStatus<T extends Status>(status: T) {
  console.log(status);
}

setStatus("loading");  // ✔
setStatus("done");     // Status 타입에 없음

열거된 값만 허용할 때 extends를 결합하면 매우 강력합니다.

 

제네릭 + keyof 조합

객체의 key만 전달받도록 제한할 때 사용합니다.

function pick<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

const user = { id: 1, name: "Bob" };
pick(user, "name");  // ✔
pick(user, "email"); // 오류: email은 key가 아님

폼 처리, API 응답 파싱 등 실무 전반에서 매우 많이 쓰이는 패턴입니다.

 

제약조건을 활용한 안전한 비교 함수

function isEqual<T extends { id: number }>(a: T, b: T) {
  return a.id === b.id;
}

isEqual({ id: 1, name: "Alice" }, { id: 1, name: "Bob" }); // ✔
isEqual({ value: 1 }, { value: 1 }); // id 속성 없음

지정된 속성이 없다면 컴파일 단계에서 오류를 잡아줄 수 있습니다.

 

제네릭 클래스에서의 extends

class Cache<T extends { id: number }> {
  private items: T[] = [];

  add(item: T) {
    this.items.push(item);
  }
}

id 속성이 없는 타입은 넣을 수 없도록 강력하게 제한됩니다. Repository, Cache, Service 레이어에서 흔히 활용됩니다.

 

특정 생성자 타입을 제약하는 방식

type Constructor = new (...args: any[]) => {};

function create<T extends Constructor>(ctor: T) {
  return new ctor();
}

클래스 타입을 인자로 받는 팩토리 패턴에서 자주 쓰입니다.

 

extends + default 타입 (일반적인 실무 패턴)

interface Response<T extends object = {}> {
  success: boolean;
  data: T;
}

const res: Response = { success: true, data: {} }; // 기본 타입 사용

제약조건과 기본값을 함께 쓰면 “확장 가능하면서 안전한 타입”을 만들 수 있습니다.

 

extends를 잘못 사용했을 때 나타나는 문제

  • 제약조건이 너무 좁으면 유연성이 떨어짐
  • 제약조건이 너무 넓으면 안전성이 떨어짐 (ex: extends any)
  • extends를 빼면 타입 흐름이 any로 흘러가 위험해짐

따라서 “해당 로직이 필요한 최소 조건만 부여”하는 것이 가장 중요합니다.

 


 

제네릭 제약조건 extends는 단순한 제한 기능을 넘어 타입 안전성, 유효성 체크, API/도메인 구조 설계 등 실무 곳곳에서 중요한 역할을 합니다.

  • extends는 T가 가져야 하는 최소 조건을 명세
  • 객체/유니온/keyof/생성자 등 다양한 타입에 활용 가능
  • 과한 제약 또는 제약 없음보다 “적절한 제약”이 핵심