
TypeScript의 유틸리티 타입은 이름 그대로 '유용한 도구'이다. 우리가 주방에서 요리할 때 다양한 도구(칼, 도마, 믹서기 등)를 사용하듯이, TypeScript에서도 타입을 더 편리하게 조작할 수 있는 여러 도구가 내장되어 있다.
이 유틸리티 타입들은 기존 타입을 변환하여 새로운 타입을 만들어 내는데, 이는 마치 우리가 원재료(감자)를 다양한 조리기구로 변형(감자칩, 매쉬드 포테이토, 감자튀김)하는 것과 비슷하다.
그럼 자주 사용되는 유틸리티 타입들을 하나씩 살펴보자.
Partial<T> - 모든 속성을 선택적으로 만들기Partial은 객체 타입의 모든 속성을 선택적(optional)으로 만든다.
장보기 목록을 생각해 보자. 필수로 사야 할 항목도 있지만, 있으면 좋지만 없어도 괜찮은 선택적인 항목들도 있다.
// 원래 장보기 목록 타입 (모든 항목이 필수)
interface ShoppingList {
milk: number; // 우유 몇 개
eggs: number; // 계란 몇 개
bread: number; // 빵 몇 개
}
// Partial을 적용한 장보기 목록 (모든 항목이 선택적)
type OptionalShoppingList = Partial<ShoppingList>;
// 이제 일부 항목만 포함해도 됩니다
const myList: OptionalShoppingList = {
milk: 1
// eggs와 bread는 선택적이므로 없어도 에러가 발생하지 않음
};
interface User {
name: string;
age: number;
email: string;
}
// 일부 정보만 업데이트할 수 있는 함수
function updateUser(userId: number, updates: Partial<User>) {
// 데이터베이스에서 사용자를 찾아 updates 객체의 속성만 업데이트
}
// 사용 예시
updateUser(123, { age: 30 }); // 나이만 업데이트
updateUser(456, { name: "김철수", email: "kim@example.com" }); // 이름과 이메일 업데이트
Partial을 사용한다.Required<T> - 모든 속성을 필수로 만들기Required는 Partial의 반대로, 객체 타입의 모든 선택적 속성을 필수로 만든다.
대학 입학 서류를 생각해 보자. 원래는 추천서가 선택사항이었는데, 정책이 바뀌어서 모든 서류가 필수가 되었다.
// 원래 입학 서류 타입 (일부 선택적)
interface AdmissionDocuments {
application: string;
transcript: string;
recommendationLetter?: string; // 선택적
}
// Required를 적용한 입학 서류 (모두 필수)
type RequiredDocuments = Required<AdmissionDocuments>;
// 이제 모든 항목이 필수!
const myDocuments: RequiredDocuments = {
application: "지원서.pdf",
transcript: "성적표.pdf",
// recommendationLetter가 없으면 에러 발생
recommendationLetter: "추천서.pdf"
};
Pick<T, K> - 특정 속성만 선택하기Pick은 객체 타입에서 특정 속성만 선택하여 새로운 타입을 만든다.
// 전체 음식점 메뉴
interface Restaurant {
burger: string;
pizza: string;
pasta: string;
salad: string;
dessert: string;
}
// 내가 좋아하는 음식만 선택 (burger, pasta, dessert)
type MyFavorites = Pick<Restaurant, "burger" | "pasta" | "dessert">;
const myMenu: MyFavorites = {
burger: "치즈버거",
pasta: "까르보나라",
dessert: "티라미수"
// salad와 pizza는 이 타입에 존재하지 않아 포함할 수 없음
};
interface APIResponse {
id: number;
name: string;
email: string;
createdAt: Date;
updatedAt: Date;
role: string;
settings: object;
}
// UI에 표시할 사용자 정보만 선택
type UserDisplay = Pick<APIResponse, "id" | "name" | "email">;
// 사용 예시
function displayUser(user: UserDisplay) {
console.log(`${user.name} (${user.email})`);
}
Omit<T, K> - 특정 속성 제외하기Omit은 Pick의 반대로, 객체 타입에서 특정 속성을 제외한 나머지로 새로운 타입을 만든다.
// 전체 재료 목록
interface Ingredients {
flour: string;
sugar: string;
egg: string;
milk: string;
peanut: string;
}
// 땅콩 알레르기가 있어서 땅콩을 제외한 재료
type AllergyFree = Omit<Ingredients, "peanut">;
const safeRecipe: AllergyFree = {
flour: "밀가루 100g",
sugar: "설탕 50g",
egg: "달걀 1개",
milk: "우유 200ml"
// peanut은 이 타입에서 제외되었으므로 포함할 수 없음
};
interface User {
id: number;
name: string;
email: string;
password: string;
token: string;
}
// 클라이언트에 전송할 안전한 사용자 정보 (비밀번호와 토큰 제외)
type SafeUser = Omit<User, "password" | "token">;
function sendUserToClient(user: User): SafeUser {
// password와 token을 제외한 정보만 반환
const { password, token, ...safeUser } = user;
return safeUser;
}
Readonly<T> - 모든 속성을 읽기 전용으로 만들기Readonly는 객체 타입의 모든 속성을 읽기 전용(수정 불가)으로 만든다.
박물관 전시품은 눈으로 보기만 할 수 있고 만지거나 수정할 수 없다.
// 박물관 전시품
interface Exhibit {
name: string;
year: number;
description: string;
}
// 읽기 전용 전시품
type ReadonlyExhibit = Readonly<Exhibit>;
const dinosaur: ReadonlyExhibit = {
name: "티라노사우루스",
year: -65000000,
description: "백악기 후기의 대형 육식 공룡"
};
// 아래 코드는 에러 발생
// dinosaur.name = "트리케라톱스"; // 읽기 전용 속성이므로 수정할 수 없음
interface Config {
apiUrl: string;
timeout: number;
debug: boolean;
}
function processConfig(config: Readonly<Config>) {
// config 객체의 속성을 읽을 수만 있고 수정할 수 없음
console.log(`API URL: ${config.apiUrl}`);
// 아래 코드는 에러 발생
// config.timeout = 5000; // 읽기 전용 속성이므로 수정할 수 없음
}
2편에서는
Record,Exclude,Extract,NonNullable,Parameters,ReturnType에 대한 설명이 이어지겠다!