유틸리티 타입
타입스크립트는 복잡한 타입 변환/조합을 쉽게 수행하도록 다양한 유틸리티 타입(Utility Types)을 내장하고 있습니다.
이 유틸리티 타입 대부분은
앞서 배운 조건부 타입과 매핑된 타입 개념(keyof, typeof, infer 등)을
기반으로 구현되어 있습니다.
유틸리티 타입은 기존 타입을 재활용하여 새로운 타입을 생성하는 데 탁월하며, 타입 정의의 반복을 줄이고 코드의 가독성 및 유지보수성을 크게 향상시킵니다. 이 절에서는 가장 자주 사용되는 주요 유틸리티 타입들을 살펴보겠습니다.
객체 타입 변환 유틸리티
유틸리티 타입에서 타입 경계, 추론 결과, 런타임 영향을 정리한 것입니다.
객체 타입의 속성을 수정하거나 특정 속성만 선택/제외할 때 사용하는 유틸리티 타입입니다.
Partial<T>: T의 모든 속성을 선택적(Optional) 으로 만듭니다.
interface User {
id: number;
name: string;
email: string;
}
type PartialUser = Partial<User>;
/*
// PartialUser의 실제 타입:
type PartialUser = {
id?: number;
name?: string;
email?: string;
};
*/
const userUpdate: PartialUser = {
name: "새로운 이름",
email: "new@example.com"
}; // id는 필수가 아님폼 데이터 업데이트 등, 객체의 일부만 필요한 경우에 유용합니다.
Required<T>: T의 모든 속성을 필수(Required)로 만듭니다. Partial의 반대입니다.
interface ProductConfig {
name?: string;
price?: number;
inStock?: boolean;
}
type FullProductConfig = Required<ProductConfig>;
/*
// FullProductConfig의 실제 타입:
type FullProductConfig = {
name: string;
price: number;
inStock: boolean;
};
*/
// const config1: FullProductConfig = { name: "Test" }; // Error: 'price' 및 'inStock' 속성이 누락됨
const config2: FullProductConfig = { name: "Monitor", price: 300000, inStock: true };Readonly<T>: T의 모든 속성을 읽기 전용(readonly) 으로 만듭니다.
interface Point {
x: number;
y: number;
}
type ImmutablePoint = Readonly<Point>;
/*
// ImmutablePoint의 실제 타입:
type ImmutablePoint = {
readonly x: number;
readonly y: number;
};
*/
const p: ImmutablePoint = { x: 10, y: 20 };
// p.x = 15; // Error: Cannot assign to 'x' because it is a read-only property.객체의 불변성을 강제할 때 사용됩니다.
Pick<T, K>: T에서 K 타입에 해당하는 속성들만 선택하여 새로운 타입을 만듭니다.
interface Todo {
id: number;
title: string;
description: string;
completed: boolean;
createdAt: Date;
}
type TodoPreview = Pick<Todo, 'id' | 'title' | 'completed'>;
/*
// TodoPreview의 실제 타입:
type TodoPreview = {
id: number;
title: string;
completed: boolean;
};
*/
const todoItem: TodoPreview = {
id: 1,
title: "타입스크립트 학습",
completed: false
};특정 API 응답에서 필요한 데이터만 추출하거나, 특정 뷰에 필요한 데이터 모델을 정의할 때 유용합니다.
Omit<T, K>: T에서 K 타입에 해당하는 속성들을 제외하여 새로운 타입을 만듭니다.
type TodoWithoutDescription = Omit<Todo, 'description'>;
/*
// TodoWithoutDescription의 실제 타입:
type TodoWithoutDescription = {
id: number;
title: string;
completed: boolean;
createdAt: Date;
};
*/
const todoDisplay: TodoWithoutDescription = {
id: 2,
title: "회의 준비",
completed: true,
createdAt: new Date()
};Pick과 상호 보완적으로 사용되며, 특정 속성을 제외한 나머지가 필요할 때 유용합니다.
Record<K, T>: K 타입의 속성 이름을 가지며, 모든 속성 값이 T 타입인 객체 타입을 만듭니다. 딕셔너리 또는 맵 타입을 정의할 때 사용됩니다.
type PageInfo = {
title: string;
url: string;
};
type Page = "home" | "about" | "contact";
type SitePages = Record<Page, PageInfo>;
/*
// SitePages의 실제 타입:
type SitePages = {
home: { title: string; url: string; };
about: { title: string; url: string; };
contact: { title: string; url: string; };
};
*/
const site: SitePages = {
home: { title: "메인 페이지", url: "/" },
about: { title: "회사 소개", url: "/about" },
contact: { title: "연락처", url: "/contact" }
};동적으로 키가 생성되거나, 특정 키 집합에 대해 일관된 값 타입을 강제할 때 강력합니다.
유니온 및 함수 타입 변환 유틸리티
유니온 타입이나 함수 타입을 다룰 때 유용한 유틸리티입니다.
Exclude<T, U>: T에서 U에 할당 가능한 타입을 제외합니다. (5장 2절 "조건부 타입"에서 설명)
type MyColors = "red" | "green" | "blue" | "black" | "white";
type PrimaryColors = Exclude<MyColors, "black" | "white">; // type PrimaryColors = "red" | "green" | "blue"Extract<T, U>: T에서 U에 할당 가능한 타입만 추출합니다. (5장 2절 "조건부 타입"에서 설명)
type MixedValue = string | number | boolean | (() => void);
type OnlyFunctions = Extract<MixedValue, Function>; // type OnlyFunctions = () => voidNonNullable<T>: T에서 null과 undefined를 제외합니다. (5장 2절 "조건부 타입"에서 설명)
type NullableString = string | null | undefined;
type NonNullString = NonNullable<NullableString>; // type NonNullString = stringParameters<T>: 함수 타입 T의 매개변수 타입을 튜플로 추출합니다.
type MyFunction = (name: string, age: number) => string;
type Params = Parameters<MyFunction>; // type Params = [name: string, age: number]
type NoParams = Parameters<() => void>; // type NoParams = []ReturnType<T>: 함수 타입 T의 반환 타입을 추출합니다.
type MyFunctionReturn = ReturnType<MyFunction>; // type MyFunctionReturn = string
type AsyncReturn = ReturnType<() => Promise<number>>; // type AsyncReturn = Promise<number>내장 유틸리티 타입은 이름은 많지만, 내부 원리는 매핑된 타입, 조건부 타입, infer 추론으로 묶어 볼 수 있습니다.
고급 유틸리티 타입 (일부)
타입스크립트에는 이 외에도 특정 상황에서 유용한 고급 유틸리티 타입들이 있습니다.
ThisParameterType<T>: 함수 타입 T의 this 매개변수 타입을 추출합니다.
function toHex(this: Number, decimal: number): string {
return this.toString(16) + decimal.toString(16);
}
type ThisType = ThisParameterType<typeof toHex>; // type ThisType = NumberOmitThisParameter<T>: 함수 타입 T에서 this 매개변수를 제거한 새로운 함수 타입을 만듭니다.
type NoThisToHex = OmitThisParameter<typeof toHex>; // type NoThisToHex = (decimal: number) => string
// const simplifiedHex: NoThisToHex = toHex; // Error: 'this' 매개 변수가 포함된 함수는 바인딩되어야 합니다.
// 타입만 제거할 뿐 런타임 동작이 변하는 것은 아닙니다.Awaited<T> (TypeScript 4.5+): Promise와 같은 awaitable 타입의 최종 resolved 값을 재귀적으로 추출합니다.
type PromiseOfNumbers = Promise<Promise<number>>;
type Result = Awaited<PromiseOfNumbers>; // type Result = number
type ValueOrPromise = string | Promise<number>;
type AwaitedValueOrPromise = Awaited<ValueOrPromise>; // type AwaitedValueOrPromise = string | number비동기 함수의 반환 타입을 정확히 추론할 때 매우 유용합니다.
아래 다이어그램은 하나의 원본 타입에서 수정 DTO, 공개 DTO, 함수 계약, 비동기 결과 타입을 파생시키는 조합을 보여줍니다.
유틸리티 타입은 원본 타입을 직접 바꾸지 않고 목적별 계약을 파생시키는 도구입니다. 다음 기준표는 API 입력, 공개 응답, 함수 계약, 비동기 결과에 맞는 유틸리티 선택을 정리합니다.
유틸리티 타입은 기존 타입을 선택, 제외, 부분화, 읽기 전용화하는 재사용 가능한 타입 변환 도구입니다. 내장 유틸리티 타입을 사용하면 반복적인 타입 선언을 줄이고, 객체 구조 변화에 맞춰 타입 계약을 더 일관되게 유지할 수 있습니다.
다음 다이어그램은 TypeScript 유틸리티 타입을 변환 대상별로 묶어 보는 선택표입니다.
이 다이어그램은 유틸리티 타입 학습을 선언 위치, 타입 좁히기, 재사용 경계 순서로 마무리합니다.
아래 다이어그램은 자주 쓰는 유틸리티 타입을 객체 변환, 유니온 필터, 함수 추출 기준으로 묶습니다.
아래 다이어그램은 유틸리티 타입을 실제 코드에 적용하기 전에 설정 위치, 타입 해석, 빌드 결과를 확인합니다.