개인의 기록
  • 소개
  • 프로젝트
  • 글
  • 링크

© 2026 newgirok

← 글 목록

맵드 타입 — 기존 타입의 모든 프로퍼티를 변환하기

2025년 11월 24일
TypeScriptMapped Types타입keyof

Partial<T>는 타입 T의 모든 프로퍼티를 선택적으로 만듭니다. 이 유틸리티 타입이 내부적으로 어떻게 동작하는지 들여다보면, 맵드 타입(Mapped Types)을 만나게 됩니다. 맵드 타입은 기존 타입의 프로퍼티를 하나씩 순회하며 변환하는 기능입니다.

기본 문법

[K in keyof T] 문법으로 타입 T의 모든 키를 순회합니다.

type ReadonlyVersion<T> = {
  readonly [K in keyof T]: T[K];
};

interface User {
  name: string;
  age: number;
}

type ReadonlyUser = ReadonlyVersion<User>;
// { readonly name: string; readonly age: number }
  • K: 현재 순회 중인 키
  • keyof T: T의 모든 키의 유니온
  • T[K]: 해당 키의 원래 값 타입

수정자 추가와 제거

프로퍼티에 readonly와 ?(선택적)를 추가하거나 -로 제거할 수 있습니다.

// 모든 프로퍼티를 선택적으로 (Partial 구현)
type MyPartial<T> = {
  [K in keyof T]?: T[K];
};

// 모든 선택적 프로퍼티를 필수로 (Required 구현)
type MyRequired<T> = {
  [K in keyof T]-?: T[K];
};

// 모든 프로퍼티를 readonly로 (Readonly 구현)
type MyReadonly<T> = {
  readonly [K in keyof T]: T[K];
};

// readonly 제거
type Mutable<T> = {
  -readonly [K in keyof T]: T[K];
};

-?는 선택적 수정자를 제거하고, -readonly는 읽기 전용 수정자를 제거합니다.

값 타입 변환

키는 유지하고 값의 타입만 바꿀 수 있습니다.

// 모든 값을 string으로
type Stringify<T> = {
  [K in keyof T]: string;
};

// 모든 값을 nullable로
type Nullable<T> = {
  [K in keyof T]: T[K] | null;
};

interface User {
  id: number;
  name: string;
}

type NullableUser = Nullable<User>;
// { id: number | null; name: string | null }

as로 키 재매핑

as 절로 키 이름을 변환할 수 있습니다.

// 모든 키에 "get" 접두사 붙이기
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

interface User {
  name: string;
  age: number;
}

type UserGetters = Getters<User>;
// { getName: () => string; getAge: () => number }

Capitalize: 문자열의 첫 글자를 대문자로 만드는 내장 유틸리티 타입. Capitalize<"name">은 "Name"이 됩니다.

조건부 키 필터링

as never로 특정 키를 제거할 수 있습니다.

// string 타입 프로퍼티만 남기기
type OnlyStrings<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K];
};

interface Mixed {
  name: string;
  age: number;
  email: string;
  isActive: boolean;
}

type StringFields = OnlyStrings<Mixed>;
// { name: string; email: string }

실전 예시 — 이벤트 핸들러 맵

interface DomEvents {
  click: MouseEvent;
  keydown: KeyboardEvent;
  scroll: Event;
}

type EventHandlers = {
  [K in keyof DomEvents as `on${Capitalize<K>}`]: (event: DomEvents[K]) => void;
};

// {
//   onClick: (event: MouseEvent) => void;
//   onKeydown: (event: KeyboardEvent) => void;
//   onScroll: (event: Event) => void;
// }

유틸리티 타입의 내부 구현

맵드 타입이 어떻게 쓰이는지 확인하는 가장 좋은 방법은 내장 유틸리티 타입의 소스를 보는 것입니다.

// TypeScript 내장 구현 (lib.es5.d.ts)
type Partial<T> = {
  [P in keyof T]?: T[P];
};

type Required<T> = {
  [P in keyof T]-?: T[P];
};

type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

type Record<K extends keyof any, T> = {
  [P in K]: T;
};

맵드 타입은 TypeScript 타입 시스템의 강력한 변환 도구입니다. 반복되는 타입 정의를 줄이고, 기존 타입과 파생 타입 사이의 동기화를 자동으로 유지할 수 있습니다.

← 이전 글keyof와 typeof — 타입에서 타입을 만드는 연산자
다음 글 →조건부 타입 — 타입 시스템의 if문