제네릭(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/생성자 등 다양한 타입에 활용 가능
- 과한 제약 또는 제약 없음보다 “적절한 제약”이 핵심
'개발 > Typescript' 카테고리의 다른 글
| [TYPESCRIPT] 제네릭 함수와 클래스 활용하기 - 재사용성과 타입 안정성을 모두 잡는 방법 (0) | 2025.12.09 |
|---|---|
| [TYPESCRIPT] 제네릭(Generic) 기초 이해하기 - 타입을 입력받는 유연한 타입 설계 (0) | 2025.12.08 |
| [TYPESCRIPT] 컴파일러 옵션 정리 — strict, noImplicitAny 등 꼭 알아야 할 핵심 옵션 (0) | 2025.12.07 |
| [TYPESCRIPT] 타입 확장과 중복 방지 패턴 - 유지보수성을 극대화하는 TypeScript 실무 전략 (0) | 2025.12.06 |
| [TYPESCRIPT] keyof와 typeof 활용하기 - 객체 기반 타입 설계를 정교하게 만드는 핵심 문법 (0) | 2025.12.05 |
