icon
5장 : 고급 타입

유틸리티 타입


타입스크립트는 복잡한 타입 변환이나 조합을 더 쉽고 간결하게 수행할 수 있도록 다양한 유틸리티 타입(Utility Types) 을 내장하고 있습니다. 이 유틸리티 타입들은 대부분 우리가 앞서 5장 2절 "조건부 타입"과 5장 3절 "매핑된 타입"에서 배운 개념들(예: 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 = () => void

NonNullable<T>: T에서 nullundefined제외합니다. (5장 2절 "조건부 타입"에서 설명)

type NullableString = string | null | undefined;
type NonNullString = NonNullable<NullableString>; // type NonNullString = string

Parameters<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>

고급 유틸리티 타입 (일부)

타입스크립트에는 이 외에도 특정 상황에서 유용한 고급 유틸리티 타입들이 있습니다.

ThisParameterType<T>: 함수 타입 Tthis 매개변수 타입을 추출합니다.

function toHex(this: Number, decimal: number): string {
  return this.toString(16) + decimal.toString(16);
}
type ThisType = ThisParameterType<typeof toHex>; // type ThisType = Number

OmitThisParameter<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

비동기 함수의 반환 타입을 정확히 추론할 때 매우 유용합니다.


유틸리티 타입은 타입스크립트의 강력한 타입 조작 능력을 보여주는 좋은 예시입니다. 이들은 복잡한 타입 변환을 한 줄의 코드로 해결할 수 있게 해주며, 재사용 가능한 타입 코드를 작성하는 데 필수적입니다. 이러한 내장 유틸리티 타입들을 잘 활용하는 것만으로도 타입스크립트 코드의 품질과 효율성을 크게 높일 수 있습니다.