아무도 가르쳐주지 않지만 반드시 알아야 할 TypeScript 유틸리티 타입 (Utility Types)
요약
TypeScript의 기본 유틸리티 타입을 넘어 실무에서 보일러플레이트 코드를 줄여주는 핵심 유틸리티 타입들을 소개합니다. Omit, Record, ReturnType 등 실제 프로젝트 경험을 바탕으로 한 활용 팁을 제공합니다.
핵심 포인트
- Omit을 활용한 효율적인 타입 필드 제거
- Record를 이용한 타입 안전한 딕셔너리 구현
- ReturnType과 Parameters를 통한 함수 시그니처 역공학
- InstanceType 및 ConstructorParameters를 활용한 클래스 타입 추출
- 문자열 리터럴 타입 변환 유틸리티 활용
아무도 가르쳐주지 않지만 반드시 알아야 할 TypeScript 유틸리티 타입 (Utility Types)
모든 TypeScript 튜토리얼은 Partial, Required, 그리고 Pick을 다룹니다. 하지만 TypeScript에는 수 시간의 보일러플레이트 (Boilerplate) 코드를 줄여줄 수 있는 유틸리티 타입 (Utility Types) 도구 모음이 가득하며, 대부분의 개발자는 이를 배우지 못합니다.
4개의 프로젝트에서 3년 동안 매일 TypeScript를 사용하며, 제가 실제로 사용하는 유틸리티 타입들을 소개합니다.
1. Omit — Pick의 역(Inverse)
Pick이 필드를 유지한다는 것을 알고 계실 겁니다. Omit은 필드를 제거합니다. 이것은 제가 가장 자주 사용하는 타입입니다.
interface User {
id: string;
name: string;
...
프로 팁 (Pro tip): 체이닝 (Chain) 하세요. Omit<User, 'id' | 'createdAt' | 'password'>는 처음부터 타입을 새로 만드는 것보다 훨씬 깔끔합니다.
2. Record — 타입 안전한 딕셔너리 (Type-Safe Dictionaries)
{ [key: string]: T } 사용을 멈추세요. Record가 더 깔끔하며 의도가 명확합니다.
// 나쁜 예 (하지만 흔함)
const statusMap: { [key: string]: number } = {
pending: 0,
...
상태 값을 잊어버리면 TypeScript가 컴파일 타임 (Compile time)에 이를 잡아냅니다. 더 이상 undefined로 인한 깜짝 놀랄 일은 없습니다.
3. ReturnType 및 Parameters — 함수 시그니처 (Function Signatures) 역공학
때로는 필요한 타입이 여러분이 작성하지 않은 함수 안에 이미 숨겨져 있을 때가 있습니다.
// 라이브러리를 사용 중이며 응답(response)의 타입을 지정하고 싶을 때
const fetchUser = async (id: string) => {
const res = await api.get(`/users/${id}`);
...
이것은 내보내진(exported) 타입을 직접 제어할 수 없는 서드파티 라이브러리 (Third-party libraries)를 사용할 때 특히 강력합니다.
4. ConstructorParameters — 타입 팩토리 함수 (Type Factory Functions)
class Database {
constructor(url: string, options: { poolSize: number }) {}
query(sql: string) { /* ... */ }
...
5. InstanceType — 클래스 생성자 (Class Constructors)에서 추출
class ErrorWithCode extends Error {
constructor(public code: number, message: string) { super(message); }
}
...
이것은 제네릭 팩토리 패턴 (Generic factory patterns)이나 의존성 주입 (Dependency injection)을 다룰 때 중요합니다.
6. Uppercase, Lowercase, Capitalize, Uncapitalize — 문자열 리터럴 타입 (String Literal Types)
TypeScript 4.1에서 이 기능들이 추가되었습니다. API 설계 시 묘하게 유용합니다.
type HttpMethod = 'get' | 'post' | 'put' | 'delete';
// 대문자로 정규화 (Normalize to uppercase)
...
7. 더 일찍 알았더라면 좋았을 것: Awaited
TypeScript 4.5에서 Awaited가 추가되었습니다. 이는 Promise 타입을 재귀적으로 언래핑 (Unwrap) 합니다.
type T1 = Awaited<Promise<string>>;
// string
...
내가 따르는 패턴
어떤 유틸리티 타입 (Utility Type)을 사용해야 할지에 대한 나의 사고 모델은 다음과 같습니다:
| 필요 사항 | 유틸리티 타입 (Utility Type) |
|---|---|
| 필드 제거 | Omit |
| ... |
이것이 중요한 이유
이것들은 단순한 지름길이 아닙니다. 이것들은 **단일 진실 공급원 (Single Sources of Truth)**을 생성합니다. User 인터페이스 (Interface)가 변경되면, 파생된 모든 타입이 자동으로 업데이트됩니다. 수동 동기화도, 불일치 (Drift)도 발생하지 않습니다.
Omit<User, 'password'>를 작성하는 개발자는, 인터페이스를 수동으로 다시 만들었다가 User에 새로운 필드가 추가되었을 때 업데이트를 잊어버리는 개발자보다 버그를 훨씬 적게 겪을 것입니다.
여러분은 매일 어떤 유틸리티 타입을 사용하시나요? 뒤늦게 발견해서 첫날부터 알았더라면 좋았을 것 같은 타입은 무엇인가요?
댓글로 공유해 주세요 — 함께 궁극의 TypeScript 유틸리티 타입 치트 시트 (Cheat Sheet)를 만들어 봅시다.
📖 더 많은 개발자 인사이트와 튜토리얼을 codcompass.com에서 확인하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기