[TS] 유틸리티 타입 (Utility Types)
01
유틸리티 타입 이란?
타입스크립트에는 반복적으로 사용되는 패턴을 쉽게 적용할 수 있는 유틸리티 타입(Utility Types) 이 제공됩니다.
이 유틸리티 타입을 활용하면 코드를 더 간결하고 안전하게 작성할 수 있어요!
유틸리티 타입은 맵드 타입(Mapped Types) 기반과 조건부 타입(Conditional Types) 기반으로 나눌 수 있습니다.
📌 맵드 타입 기반 → 객체의 속성을 변형하는 유틸리티 입
- 예: Partial<T>, Required<T>, Readonly<T>, Pick<T>, Omit<T> 등
- 객체의 특정 속성을 선택적으로 만들거나(Readonly, Partial), 필수로 변경(Required) 하는 등의 변형을 쉽게 수행할 수 있습니다.
📌 조건부 타입 기반 → 특정 타입을 제거하거나 추출하는 유틸리티 타입
- 예: Exclude<T, U>, Extract<T, U>, ReturnType<T> 등
- 타입 연산을 동적으로 수행하여 특정 조건에 맞는 타입을 선택하거나 제거할 수 있습니다.
02
Partial <T> – 객체 속성을 선택적으로 만들기
기존 객체의 모든 속성을 선택 사항(optional)으로 변환합니다.
📌 언제 사용할까?
: 아직 모든 정보가 입력되지 않은 상태에서 임시 데이터를 저장할 때 사용합니다.
예를 들어, 폼 입력값이 완성되지 않은 상태에서 저장할 때
interface Post {
title: string;
content: string;
}
const draftPost: Partial<Post> = {
title: "임시 제목", // 내용은 아직 작성 안 함
};
✔️ 모든 속성이 선택적(optional)으로 변함
✔️ 필요한 데이터만 저장할 수 있음
03
Required <T> – 모든 속성을 필수로 만들기
기존 객체의 모든 속성을 필수(required)로 변환합니다.
📌 언제 사용할까?
: 기본적으로 선택 가능했던 값을 특정 상황에서는 반드시 입력하도록 강제하고 싶을 때 사용합니다.
예를 들어, 회원 가입 시 모든 정보를 필수로 입력해야 할 때
interface User {
name: string;
email?: string; // 기본적으로 선택 사항
}
const fullUser: Required<User> = {
name: "홍길동",
email: "hong@example.com", // 이제 무조건 입력해야 함!
};
✔️ 모든 속성이 필수(required) 속성이 됨
✔️ 빠뜨리면 안 되는 중요한 데이터를 강제할 때 유용함
04
Readonly <T> – 객체 수정 막기
객체 속성을 읽기 전용(readonly)으로 만들어 수정할 수 없도록 제한합니다.
📌 언제 사용할까?
: 변경하면 안 되는 데이터(예: 환경설정, 사용자 정보)를 보호할 때 사용합니다.
예를 들어, 환경 설정 값이 실수로 변경되지 않도록 보호해야 할 때
interface User {
name: string;
age: number;
}
const user: Readonly<User> = {
name: "철수",
age: 25,
};
user.name = "영희"; // ❌ 오류 발생! Readonly 타입은 수정할 수 없음
✔️ 객체의 속성을 실수로 변경하는 걸 방지할 수 있음
✔️ 보호해야 하는 데이터를 안전하게 관리할 수 있음
05
Record <K, V> – 객체의 키와 값을 한 번에 정의하기
객체의 키(Key)와 값(Value) 타입을 미리 지정할 수 있습니다.
📌 언제 사용할까?
: 객체의 키와 값의 타입을 미리 정해야 할 때사용합니다.
예를 들어, 언어별 메시지나 설정값을 저장할 때
const greetings: Record<"ko" | "en" | "jp", string> = {
ko: "안녕하세요",
en: "Hello",
jp: "こんにちは",
};
✔️ 키와 값 타입을 명확하게 지정할 수 있음
✔️ 다양한 설정값을 객체로 관리할 때 유용함
Pick <T, K> – 필요한 속성만 골라서 사용하기
기존 객체에서 특정 속성만 골라서 새로운 타입을 생성합니다.
📌 언제 사용할까?
: 필요한 속성만 선택하여 특정 데이터 구조를 만들고 싶을 때 사용합니다.
예를 들어, 프로필 화면에서는 이름과 이메일만 필요하고, 나머지는 필요 없을 때
interface User {
name: string;
email: string;
password: string;
}
const userProfile: Pick<User, "name" | "email"> = {
name: "영희",
email: "younghee@example.com",
};
✔️ 필요한 속성만 선택해서 사용할 수 있음
✔️ 보안상 중요한 정보(password 등)를 제외할 때 유용함
07
Omit <T, K> – 특정 속성만 빼고 사용하기
기존 객체에서 특정 속성만 제외하고 새로운 타입을 생성합니다.
📌 언제 사용할까?
: 특정 데이터를 제외하고 객체를 생성해야 할 때 사용합니다.
예를 들어, 회원 정보를 사용할 때 비밀번호 같은 민감한 정보를 숨기고 싶을 때
interface User {
name: string;
email: string;
password: string;
}
const publicUser: Omit<User, "password"> = {
name: "철수",
email: "chulsoo@example.com",
};
✔️ 중요하지 않거나 민감한 정보(password 등)를 쉽게 제거할 수 있음
✔️ 객체에서 특정 속성을 빼고 새로운 타입을 만들 때 유용함
08
Exclude <T, U> – 특정 타입을 제외하기
타입에서 특정 타입만 제거합니다.
📌 언제 사용할까?
: 불필요한 타입을 제거하고 싶을 때 사용합니다.
예를 들어, 로딩 상태를 제외한 상태 값만 필요할 때
type Status = "success" | "error" | "loading";
type WithoutLoading = Exclude<Status, "loading">;
// "success" | "error"
✔️ 원하는 타입만 골라서 사용할 수 있음
✔️ 타입에서 특정 값만 제거할 때 유용함
09
Extract <T, U> – 특정 타입만 추출하기
타입에서 특정 타입만 남기고 추출합니다.
📌 언제 사용할까?
: 필요한 타입만 유지하고 싶을 때 사용합니다.
예를 들어, 특정 상태 값만 필터링해서 추출하여 사용할 때
type Status = "success" | "error" | "loading";
type OnlyLoading = Extract<Status, "loading">;
// "loading"
✔️ 필요한 타입만 유지하고 나머지는 제거할 수 있음
✔️ 타입을 필터링할 때 유용함
10
ReturnType <T> – 함수의 반환값 타입 가져오기
함수의 반환값 타입을 자동으로 가져옵니다.
📌 언제 사용할까?
: 함수의 반환값을 다른 곳에서 재사용하고 싶을 때 사용합니다.
예를 들어, 함수의 결과를 특정 타입으로 지정해야 할 때
function getAge() {
return 30;
}
type Age = ReturnType<typeof getAge>; // `number` 타입이 자동으로 추출됨
✔️ 함수의 반환 타입을 자동으로 가져와서 일관성을 유지할 수 있음
✔️ 함수의 반환값 타입이 바뀌어도 자동으로 적용됨
11
마무리
🎯 정리 – 언제 어떤 유틸리티 타입을 사용할까?
유틸리티 타입 | 사용 목적 | |
맵드 타입 기반 | Partial<T> | 객체 속성을 선택적(Optional) 으로 만들 때 |
Required<T> | 객체 속성을 모두 필수(Required) 로 만들 때 | |
Readonly<T> | 객체 속성을 읽기 전용(Readonly) 으로 만들어 변경을 방지할 때 | |
Record<K, V> | 객체의 키와 값 타입을 미리 정의할 때 | |
Pick<T, K> | 특정 속성만 선택해서 사용할 때 | |
Omit<T, K> | 특정 속성만 제거해서 사용할 때 | |
조건부 타입 기반 | Exclude<T, U> | 특정 타입을 제외할 때 |
Extract<T, U> | 특정 타입만 추출할 때 | |
ReturnType<T> | 함수의 반환 타입을 자동으로 가져올 때 |