frontend
TypeScript Utility Types 완벽 가이드 — Pick, Omit, Partial 실전 활용
TypeScript의 Pick, Omit, Partial, Required, Readonly, Record, ReturnType 등 내장 Utility Types를 실전 예제와 함께 언제 쓰는지 기준을 정리하였다.
- ·Partial<T>은 T의 모든 프로퍼티를 optional로 만들고 Required<T>는 모두 필수로 만든다
- ·Pick<T, K>는 T에서 K 키들만 골라 새 타입을 만들고 Omit<T, K>는 K를 제외한 나머지로 만든다
- ·Record<K, V>는 키 타입 K와 값 타입 V로 구성된 객체 타입을 만들며 딕셔너리 패턴에 유용하다
- ·ReturnType<T>는 함수 타입 T의 반환 타입을 추출하며 API 응답 타입을 재사용할 때 편리하다
폼 수정 컴포넌트를 만들 때 원본 User 타입의 모든 필드를 optional로 만들어야 해서 Partial을 처음 써봤다. 타입을 일일이 복붙해서 수정하다가 원본 타입이 바뀔 때마다 수정 폼 타입도 같이 고쳐야 하는 번거로움이 반복됐는데, Partial<User>로 바꾸자 원본 타입 변경이 자동으로 반영돼 그 이후로 타입 유틸리티를 적극적으로 활용하게 됐다.
자주 쓰는 TypeScript Utility Types
TypeScript Partial과 Required — 폼과 업데이트 API 타입에 활용하기
Partial<T>는 T의 모든 프로퍼티에 ?를 붙여 선택적으로 만든다. PATCH 요청처럼 일부 필드만 전달하는 업데이트 API의 요청 바디 타입이나 폼의 초기 상태에 자주 활용된다. Required<T>는 반대로 모든 optional 프로퍼티에서 ?를 제거해 필수로 만든다. 서드파티 라이브러리 타입에 너무 많은 optional이 있어 불편할 때 Required로 래핑하면 타입 검사를 강화할 수 있다. 중첩 객체의 경우 Partial은 최상위 레벨만 optional로 만들므로 깊은 중첩까지 optional이 필요하다면 DeepPartial을 직접 구현하거나 유틸리티 라이브러리를 사용해야 한다. 폼 라이브러리인 react-hook-form에서도 내부적으로 DeepPartial 패턴을 사용해 폼 값 타입을 추론한다.
interface User {
id: number
name: string
email: string
}
type UpdateUserDto = Partial<User> // { id?: number; name?: string; email?: string }
type StrictConfig = Required<Partial<Config>> // 모든 필드 필수화TypeScript Pick과 Omit — 불필요한 타입 중복 없이 파생 타입 만들기
Pick<T, K>는 T에서 필요한 키만 골라 새 타입을 만들고, Omit<T, K>는 T에서 특정 키를 제외한 나머지로 새 타입을 만든다. API 응답 타입에서 비밀번호처럼 클라이언트에 노출하면 안 되는 필드를 제외할 때 Omit이 유용하고, 목록 아이템에 필요한 최소 필드만 골라 렌더링 컴포넌트의 props 타입으로 쓸 때 Pick이 맞다. Omit은 내부적으로 Pick과 Exclude를 조합해 구현되며 선택 제외 키가 T에 존재하지 않아도 TypeScript가 오류를 내지 않는다는 점을 주의해야 한다. 이 특성 때문에 오타로 잘못된 키를 지정했을 때 조용히 넘어가는 경우가 있다. 정확한 타입 안전성이 필요하면 Omit 대신 Exclude와 keyof를 직접 조합하거나 @ts-expect-error를 활용해 검증하는 방법을 사용한다.
TypeScript Record와 조건부 타입
TypeScript Record로 타입 안전한 딕셔너리와 열거형 맵 구성하기
Record<K, V>는 키 타입 K와 값 타입 V로 구성된 객체 타입을 정의한다. { [key: string]: V } 형태의 인덱스 시그니처와 달리 K를 유니온 타입이나 리터럴 타입으로 지정하면 정의된 키 외에 다른 키를 사용하면 오류가 발생하여 더 엄격한 타입 검사가 가능하다. 예를 들어 Record<'ko' | 'en', string>으로 선언하면 'ja' 같은 미정의 키 접근을 컴파일 타임에 잡아낸다. 상태 코드별 메시지 맵, 카테고리별 아이콘 맵, locale별 번역 맵 등 열거형 키와 값의 1:1 대응을 표현할 때 Record가 가독성 높은 코드를 만든다. i18n 메시지 타입을 Record<LocaleKey, string>으로 정의해두면 새 언어를 추가할 때 누락된 번역 키를 자동으로 감지할 수 있다.
type Status = 'pending' | 'success' | 'error'
const statusLabel: Record<Status, string> = {
pending: '처리 중',
success: '완료',
error: '오류'
// 누락 시 컴파일 에러
}함수 타입 관련 Utility Types
TypeScript ReturnType과 Parameters로 함수 시그니처 재사용하기
ReturnType<typeof fn>은 함수 fn의 반환 타입을 추출한다. API 클라이언트 함수가 반환하는 타입을 별도 인터페이스로 중복 선언하지 않고 ReturnType으로 참조하면 함수 시그니처가 바뀔 때 타입도 자동으로 동기화된다. Parameters<typeof fn>는 함수의 파라미터 튜플 타입을 추출하며, 함수를 래핑하는 고차 함수에서 원본 함수의 파라미터 타입을 그대로 전달할 때 유용하다. Awaited<ReturnType<typeof asyncFn>>처럼 조합하면 async 함수의 실제 반환 타입(Promise 내부 타입)을 정확히 얻을 수 있다. 이 유틸리티들을 잘 조합하면 타입 선언 중복이 크게 줄어 원본 코드 변경이 타입에 자동 반영되는 자기 관리형 타입 시스템을 만들 수 있다.
자주 묻는 질문
Partial과 Omit을 조합해서 특정 필드만 선택적으로 만들 수 있나요?+
가능합니다. Pick<T, '필수키'> & Partial<Pick<T, '선택키'>> 형태로 조합하면 일부 필드만 optional인 타입을 만들 수 있습니다. 이 패턴은 폼 타입 설계에 자주 활용됩니다.
keyof typeof 패턴은 언제 사용하나요?+
const obj = { a: 1, b: 2 }처럼 const 객체에서 키 유니온 타입을 추출할 때 keyof typeof obj를 씁니다. Record의 키 타입이나 switch 분기 타입 가드에 유용합니다.
타입스크립트 infer 키워드는 Utility Types와 어떻게 관련이 있나요?+
ReturnType, Parameters 같은 내장 Utility Types가 내부적으로 infer를 사용해 타입을 추출합니다. type ReturnType<T> = T extends (...args: any) => infer R ? R : never 형태로 구현되어 있습니다.