TypeScript 유틸리티 타입
TypeScript는 기존 타입을 변환하거나 조합할 수 있는 유틸리티 타입을 내장으로 제공합니다. 반복적인 타입 선언을 줄이고, 타입 간의 관계를 명확하게 표현할 수 있어 코드 품질이 크게 올라갑니다.
1. Partial<T>
모든 프로퍼티를 선택적(optional) 으로 만듭니다.
interface User {
id: number;
name: string;
email: string;
}
// 수정 API payload처럼, 일부 필드만 전달해도 되는 경우
function updateUser(id: number, fields: Partial<User>) {
// fields.name만 있어도 OK
}
updateUser(1, { name: '이범희' }); // ✅
내부 구현은 다음과 같습니다.
type MyPartial<T> = {
[K in keyof T]?: T[K];
};
2. Required<T>
모든 프로퍼티를 필수로 만듭니다. Partial의 반대입니다.
interface Config {
host?: string;
port?: number;
ssl?: boolean;
}
// 초기화 완료 후에는 모든 값이 반드시 존재해야 한다고 선언
function startServer(config: Required<Config>) {
console.log(`${config.host}:${config.port}`);
}
3. Readonly<T>
모든 프로퍼티를 읽기 전용으로 만듭니다. 불변성을 강제하고 싶을 때 사용합니다.
const user: Readonly<User> = {
id: 1,
name: '이범희',
email: 'beomhui@promtend.com',
};
user.name = '다른이름'; // ❌ Error: 읽기 전용 속성이므로 할당 불가
배열에 적용하면 push, pop 등의 변경 메서드도 막을 수 있습니다.
const items: ReadonlyArray<string> = ['a', 'b', 'c'];
items.push('d'); // ❌ Error
4. Pick<T, K>
타입 T에서 특정 키만 골라 새로운 타입을 만듭니다.
interface Article {
id: number;
title: string;
content: string;
author: string;
createdAt: Date;
}
// 목록 페이지에서는 content가 필요 없다
type ArticleListItem = Pick<Article, 'id' | 'title' | 'author' | 'createdAt'>;
5. Omit<T, K>
타입 T에서 특정 키를 제외한 새로운 타입을 만듭니다. Pick의 반대입니다.
// DB에서 id와 createdAt은 자동 생성 → 입력받지 않음
type CreateArticleInput = Omit<Article, 'id' | 'createdAt'>;
const input: CreateArticleInput = {
title: '새 글',
content: '내용...',
author: '이범희',
};
Pick은 "이것만 남긴다",Omit은 "이것만 뺀다" 라고 기억하면 쉽습니다.
6. Record<K, V>
키 타입 K와 값 타입 V로 객체 맵을 만듭니다.
type Role = 'admin' | 'editor' | 'viewer';
const permissions: Record<Role, string[]> = {
admin: ['read', 'write', 'delete'],
editor: ['read', 'write'],
viewer: ['read'],
};
API 응답을 id 기준으로 인덱싱할 때도 자주 쓰입니다.
type UserMap = Record<number, User>;
const usersById: UserMap = {
1: { id: 1, name: '이범희', email: 'a@b.com' },
2: { id: 2, name: '홍길동', email: 'c@d.com' },
};
7. Exclude<T, U> / Extract<T, U>
유니온 타입에서 특정 멤버를 제거(Exclude) 하거나 추출(Extract) 합니다.
type Status = 'pending' | 'active' | 'banned' | 'deleted';
// banned와 deleted를 제외한 활성 상태만
type ActiveStatus = Exclude<Status, 'banned' | 'deleted'>;
// → 'pending' | 'active'
// 문자열 타입만 추출
type StringOrNumber = string | number | boolean;
type OnlyString = Extract<StringOrNumber, string>;
// → string
8. NonNullable<T>
타입에서 null과 undefined를 제거합니다.
type MaybeString = string | null | undefined;
type DefinitelyString = NonNullable<MaybeString>;
// → string
function process(value: NonNullable<MaybeString>) {
console.log(value.toUpperCase()); // null 걱정 없이 사용 가능
}
9. ReturnType<T>
함수 타입 T의 반환 타입을 추출합니다.
function fetchUser() {
return { id: 1, name: '이범희' };
}
type FetchUserResult = ReturnType<typeof fetchUser>;
// → { id: number; name: string; }
라이브러리 함수의 반환 타입을 재사용할 때 특히 유용합니다.
// Redux의 useSelector 타입을 추출하는 패턴
type RootState = ReturnType<typeof store.getState>;
10. Parameters<T>
함수 타입 T의 매개변수 타입을 튜플로 추출합니다.
function createUser(name: string, age: number, role: Role) { /* ... */ }
type CreateUserParams = Parameters<typeof createUser>;
// → [name: string, age: number, role: Role]
// 첫 번째 파라미터 타입만 꺼내기
type FirstParam = Parameters<typeof createUser>[0];
// → string
실전 조합 패턴
유틸리티 타입은 조합해서 사용할 때 진짜 위력이 발휘됩니다.
interface Post {
id: string;
title: string;
content: string;
isDraft: boolean;
authorId: string;
createdAt: Date;
updatedAt: Date;
}
// 생성 시: id, 날짜는 서버 생성, authorId는 세션에서
type CreatePostInput = Omit<Post, 'id' | 'authorId' | 'createdAt' | 'updatedAt'>;
// 수정 시: title과 content만 수정 가능하고, 모두 선택적으로
type UpdatePostInput = Partial<Pick<Post, 'title' | 'content' | 'isDraft'>>;
// 목록 응답: content 제외
type PostSummary = Omit<Post, 'content'>;
이렇게 하면 별도의 인터페이스를 여러 개 중복 선언하지 않아도 됩니다.
정리
| 유틸리티 타입 | 역할 |
| --- | --- |
| Partial<T> | 모든 프로퍼티를 선택적으로 |
| Required<T> | 모든 프로퍼티를 필수로 |
| Readonly<T> | 모든 프로퍼티를 읽기 전용으로 |
| Pick<T, K> | 특정 키만 골라 새 타입 생성 |
| Omit<T, K> | 특정 키를 제외한 새 타입 생성 |
| Record<K, V> | 키-값 매핑 타입 생성 |
| Exclude<T, U> | 유니온에서 특정 타입 제거 |
| Extract<T, U> | 유니온에서 특정 타입 추출 |
| NonNullable<T> | null/undefined 제거 |
| ReturnType<T> | 함수 반환 타입 추출 |
| Parameters<T> | 함수 파라미터 타입 추출 |
유틸리티 타입을 잘 활용하면 타입 선언의 중복이 줄어들고, 원본 타입을 수정할 때 파생 타입들이 자동으로 따라와서 유지보수가 훨씬 쉬워집니다.