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

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)
  • 블로그 메뉴

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

  • 공지사항

  • 인기 글

  • 태그

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

  • 최근 글

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

티스토리툴바