타입스크립트 유틸리티 타입

Frontend

유틸리티 타입은 타입스크립트가 자체적으로 제공하는 특수한 타입들로, 제네릭, 맵드 타입, 조건부 타입 등을 이용해 실무에서 자주 사용되는 타입들을 모아 놓은 것이다.

 

예를 들어 Readonly<T>로 모든 프로퍼티를 읽기 전용으로 만들 수 있다.

interface Person {
  name: string;
  hp: number;
}

const person: Readonly<Person> = {
  name: "devmark",
  hp: 30
};

person.name = ''; // ❌ 읽기 전용 프로퍼티

 

Partial<T>로 모든 프로퍼티를 선택적으로 만들 수도 있다.

const person: Partial<Person> = {
  name: "devmark",
};

 

이 글에서는 실무에서 자주 사용되는 유틸리티 타입들을 직접 구현하며 동작 원리를 이해해보려고 한다.

 

더 많은 유틸리티 타입은 TypeScript 공식 문서에서 확인할 수 있다.

 

맵드 타입 기반 유틸리티 타입

 

[TS] 맵드 타입

맵드 타입(Mapped Type)은 기존 객체 타입을 기반으로 새로운 객체 타입을 만드는 기능이다. 유저 정보를 관리하는 함수들을 작성해보자.interface User { id: number; name: string; age: number;}function fetchUser(): U

devmark.tistory.com

Partial

Partial<T>는 객체 타입의 모든 프로퍼티를 선택적 프로퍼티로 변환한다.

 

블로그 플랫폼을 구현한다고 가정해보자.

interface Post {
  title: string;
  tags: string[];
  content: string;
  thumbnailURL?: string;
}

const draft: Post = { // ❌ tags 프로퍼티가 없음
  title: "제목은 나중에 짓자...",
  content: "초안...",
};

임시 저장 게시글은 일부 정보가 아직 설정되지 않았다. 그렇다고 Post 타입의 모든 프로퍼티를 선택적으로 만들면 완성된 게시글도 불완전한 상태를 허용하게 된다.

 

Partial을 적용해보자.

const draft: Partial<Post> = {
  title: "제목 나중에 짓자",
  content: "초안...",
};

 

 

직접 구현해보기

일단 하나의 타입 변수 T를 사용하는 제네릭 타입인 것은 확실하다.

type Partial<T> = any;

 

다음으로는 T에 할당된 객체 타입의 모든 프로퍼티를 선택적 프로퍼티로 바꿔줘야 한다.
기존 객체 타입을 다른 타입으로 변환하는 맵드 타입을 이용해 다음과 같이 수정한다.

type Partial<T> = {
  [key in keyof T]?: T[key];
}

 

Required

Required<T>는 객체 타입의 모든 프로퍼티를 필수 프로퍼티로 변환한다.

 

썸네일이 반드시 있어야 하는 게시글이 필요하다고 가정해보자.

const withThumbnailPost: Post = {
  title: "유틸리티 타입",
  tags: ["ts"],
  content: "",
  thumbnailURL: "https://...",
};

Post의 thumbnailURL이 선택적 프로퍼티라서 실수로 삭제해도 오류가 발생하지 않는다.

 

Required를 적용해보자.

const withThumbnailPost: Required<Post> = {
  title: "유틸리티 타입",
  tags: ["ts"],
  content: "",
  thumbnailURL: "https://...", // 필수
};

 

 

직접 구현해보기

일단 기존의 모든 프로퍼티를 포함하는 제네릭 맵드 타입으로 만들어준다.

type Required<T> = {
  [key in keyof T]: T[key];
};

 

그리고 이제 모든 프로터피가 필수 프로퍼티가 되도록 만들어야 한다.
모든 프로퍼티를 필수 프로퍼티로 만든다는 것은, 반대로 모든 프로퍼티에서 "선택적"이라는 기능을 제거하는 것과 같다.
따라서 다음과 같이 -?를 프로퍼티 이름 뒤에 붙여주면 된다.

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

-?는 ?가 붙어있는 선택적 프로퍼티가 있으면 ?를 제거하라는 의미이다.

 

Readonly

Readonly<T>는 객체 타입의 모든 프로퍼티를 읽기 전용으로 변환한다.

 

절대 수정할 수 없는 보호된 게시글이 필요하다고 가정해보자.

const readonlyPost: Post = {
  title: "보호된 게시글입니다.",
  tags: [],
  content: "",
};

readonlyPost.content = '해킹당함'; // 막을 수 없음

 

Readonly를 적용해보자.

const readonlyPost: Readonly<Post> = {
  title: "보호된 게시글입니다.",
  tags: [],
  content: "",
};

readonlyPost.content = '해킹당함'; // ❌

 

 

직접 구현해보기

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

 

Pick

Pick<T, K>는 객체 타입에서 특정 프로퍼티만 골라낸다.

 

태그 기능이 추가되기 전에 작성된 옛날 게시글이 있다고 가정해보자.

const legacyPost: Post = { // ❌ tags 프로퍼티 필요
  title: "옛날 글",
  content: "옛날 컨텐츠",
};

옛날 게시글만을 위한 타입을 별도로 만들 수도 없고, 일일이 tags를 추가할 수도 없다.

 

Pick을 적용해보자.

const legacyPost: Pick<Post, "title" | "content"> = {
  title: "옛날 글",
  content: "옛날 컨텐츠",
};

 

 

직접 구현해보기

객체 타입을 변형하는 타입이므로 맵드 타입을 이용해 만들 수 있다.

일단 2개의 타입 변수 T와 K를 사용하는 타입이므로 다음과 같이 정의한다.

type Pick<T, K> = any;

 

T로부터 K프로퍼티만 뽑아낸 객체 타입을 만들어야 하므로 다음과 같이 맵드 타입으로 정의한다.

type Pick<T, K> = {
  [key in K]: T[key];
};

 

마지막으로 K가 T의 key로만 이루어진 String Literal Union타입임을 보장할 수 있도록 제약을 추가한다.

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

 

 

Omit

Omit<T, K>는 객체 타입에서 특정 프로퍼티만 제거한다.

 

제목이 없는 게시글도 존재할 수 있다고 가정해보자.

const noTitlePost: Post = { // ❌ title 프로퍼티 필요
  content: "",
  tags: [],
  thumbnailURL: "",
};

 

Omit을 적용해보자.

const noTitlePost: Omit<Post, "title"> = {
  content: "",
  tags: [],
  thumbnailURL: "",
};

 

 

직접 구현해보기

먼저 2개의 타입 변수를 사용하는 제네릭 타입이므로 다음과 같이 정의한다.

type Omit<T, K> = any;

 

그 다음 앞서 Pick타입에서 했던 것과 같이 K에 제약을 추가한다.

type Omit<T, K extends keyof T> = any;

 

그리고 앞서 만든 Pick타입을 이용해 다음과 같이 완성한다.

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

 


Omit<Post, "title">의 동작 과정:

  1. keyof Post → "title" | "content" | "tags" | "thumbnailURL"
  2. Exclude<"title" | "content" | "tags" | "thumbnailURL", "title"> → "content" | "tags" | "thumbnailURL"
  3. Pick<Post, "content" | "tags" | "thumbnailURL"> → title이 제거된 타입

 

Record

Record<K, V>는 키와 값의 타입을 지정하여 객체 타입을 생성한다.

 

화면 크기에 따라 3가지 버전의 썸네일을 지원한다고 가정해보자.

type Thumbnail = {
  large: {
    url: string;
  };
  medium: {
    url: string;
  };
  small: {
    url: string;
  };
};

이 코드는 버전이 추가될때마다 중복 코드가 발생하게 된다.

 

Record를 적용해보자.

type Thumbnail = Record
  "large" | "medium" | "small",
  { url: string }
>;

 

직접 구현해보기

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

K extends keyof any는 K가 객체의 키로 사용할 수 있는 타입(string, number, symbol)임을 보장한다.

 

조건부 타입 기반 유틸리티 타입

 

[TS] 분산적인 조건부 타입

분산적인 조건부 타입type StringNumberSwitch = T extends number ? string : number;let a: StringNumberSwitch; // stringlet b: StringNumberSwitch; // number 위 조건부 타입에 Union을 할당해 보자.let c: StringNumberSwitch; // string | numb

devmark.tistory.com

Exclude

Exclude<T, U>는 Union 타입 T에서 U를 제거한다.

type A = Exclude<string | boolean, string>; // boolean

 

조건부 타입과 분산을 이용해 구현할 수 있다.

type Exclude<T, U> = T extends U ? never : T;

 

 

Extract

Extract<T, U>는 Union 타입 T에서 U만 추출한다.

type B = Extract<string | boolean, boolean>; // boolean

 

직접 구현하면 이런 모습이다.

type Extract<T, U> = T extends U ? T : never;

 

ReturnType

ReturnType<T>는 함수 타입의 반환값 타입을 추출한다.

function funcA() {
  return "hello";
}

function funcB() {
  return 10;
}

type ReturnA = ReturnType<typeof funcA>; // string
type ReturnB = ReturnType<typeof funcB>; // number

 

직접 구현하자면, infer를 사용해 반환 타입을 추론할 수 있다.

type ReturnType<T extends (...args: any) => any> = T extends (
  ...args: any
) => infer R
  ? R
  : never;

 

'Frontend' 카테고리의 다른 글

Zustand로 전역 상태 관리하기  (0) 2025.11.18
aria-label과 텍스트 콘텐츠  (0) 2025.11.13
웹 애플리케이션의 렌더링 방식과 Next.js  (0) 2025.11.08
타입스크립트 타입 시스템: 계층과 호환성  (0) 2025.10.31
타입스크립트의 동작 원리  (0) 2025.10.27
'Frontend' 카테고리의 다른 글
  • aria-label과 텍스트 콘텐츠
  • 웹 애플리케이션의 렌더링 방식과 Next.js
  • 타입스크립트 타입 시스템: 계층과 호환성
  • 타입스크립트의 동작 원리
고견
고견
개발 자국 남기기
  • 고견
    개발자국
    고견
  • 전체
    오늘
    어제
    • 분류 전체보기 (157) N
      • Frontend (29)
        • Next.js (16)
        • JavaScript (7)
      • CS (19) N
        • 자료구조 (9)
        • 알고리즘 (5)
        • 운영체제 (4) N
        • 네트워크 (1) N
      • TIL (93)
      • Dev Log (16)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    클래스
    ai 감성 일기장
    CS
    자료구조
    타입 좁히기
    배열
    useState
    알고리즘
    인터페이스
    generic
    cs50
    react
    Pages Router
    함수 타입
    javascript
    제네릭
    Trouble Shooting
    앱 라우터
    C
    바닐라 자바스크립트
    App Router
    algorithm
    Next.js
    트러블 슈팅
    emotion diary
    typescript
    Spa
    문자열
    페이지 라우터
    memory
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
고견
타입스크립트 유틸리티 타입
상단으로

티스토리툴바