유틸리티 타입이란?
기존 타입을 변환해 새 타입을 만드는 내장 도구입니다. 반복적인 타입 정의를 줄여줍니다.
interface User {
id: number;
name: string;
email: string;
password: string;
createdAt: Date;
}
Partial<T>: 모든 속성을 선택적으로
type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string; ... }
// PATCH 요청: 일부 필드만 업데이트
function updateUser(id: number, changes: Partial<User>): User {
// changes는 어떤 필드든 있어도 없어도 됨
return { ...existingUser, ...changes };
}
updateUser(1, { name: "새 이름" }); // ✅
updateUser(1, { email: "new@example.com", name: "새 이름" }); // ✅
Required<T>: 모든 속성을 필수로
interface Config {
host?: string;
port?: number;
debug?: boolean;
}
type RequiredConfig = Required<Config>;
// { host: string; port: number; debug: boolean; }
Readonly<T>: 모든 속성을 읽기 전용으로
type ReadonlyUser = Readonly<User>;
const user: ReadonlyUser = {
id: 1, name: "철수", email: "kim@example.com",
password: "secret", createdAt: new Date(),
};
// user.name = "영희"; // ❌ 읽기 전용
Pick<T, K>: 특정 속성만 선택
// 공개 프로필: 민감한 정보 제외
type PublicUser = Pick<User, "id" | "name" | "email">;
// { id: number; name: string; email: string; }
// API 응답에서 필요한 필드만
type UserSummary = Pick<User, "id" | "name">;
Omit<T, K>: 특정 속성 제외
// 비밀번호와 생성일 제외
type SafeUser = Omit<User, "password" | "createdAt">;
// { id: number; name: string; email: string; }
// 새 사용자 생성: ID는 서버에서 생성
type CreateUserDto = Omit<User, "id" | "createdAt">;
Record<K, V>: 키-값 맵 타입
// 키: string, 값: number인 객체
type ScoreMap = Record<string, number>;
const scores: ScoreMap = { 철수: 85, 영희: 92 };
// 열거형 키
type Status = "active" | "inactive" | "pending";
type StatusLabel = Record<Status, string>;
const labels: StatusLabel = {
active: "활성",
inactive: "비활성",
pending: "대기",
};
Exclude<T, U>와 Extract<T, U>
type AllStatus = "active" | "inactive" | "pending" | "banned";
// banned를 제외
type NormalStatus = Exclude<AllStatus, "banned">;
// "active" | "inactive" | "pending"
// 특정 상태만 추출
type ActiveOrPending = Extract<AllStatus, "active" | "pending">;
// "active" | "pending"
ReturnType<T>와 Parameters<T>
function fetchUser(id: number, token: string): Promise<User> {
return fetch(`/api/users/${id}`).then(r => r.json());
}
// 함수 반환 타입 추출
type FetchUserReturn = ReturnType<typeof fetchUser>;
// Promise<User>
// 함수 매개변수 타입 추출
type FetchUserParams = Parameters<typeof fetchUser>;
// [id: number, token: string]
실전 조합 패턴
// CRUD 작업에서 자주 쓰는 패턴
interface Product {
id: string;
name: string;
price: number;
stock: number;
createdAt: Date;
updatedAt: Date;
}
// 생성: id, 날짜는 서버에서 생성
type CreateProductDto = Omit<Product, "id" | "createdAt" | "updatedAt">;
// 수정: 모든 필드 선택적 (id는 경로 파라미터로)
type UpdateProductDto = Partial<Omit<Product, "id" | "createdAt" | "updatedAt">>;
// 응답: 전체
type ProductResponse = Readonly<Product>;
// 목록: 일부만
type ProductSummary = Pick<Product, "id" | "name" | "price">;
정리
| 유틸리티 타입 | 변환 내용 |
|---|---|
Partial<T> | 모든 속성 선택적 |
Required<T> | 모든 속성 필수 |
Readonly<T> | 모든 속성 읽기 전용 |
Pick<T, K> | 특정 속성만 선택 |
Omit<T, K> | 특정 속성 제외 |
Record<K, V> | 키-값 맵 타입 |
Exclude<T, U> | 유니온에서 특정 타입 제거 |
Extract<T, U> | 유니온에서 특정 타입 추출 |
ReturnType<T> | 함수 반환 타입 추출 |
다음 편에서는 타입 좁히기와 고급 패턴 — 조건부 타입, 맵드 타입, 타입 가드를 배웁니다.