유틸리티 타입

Raccoon·2025년 5월 28일

본 포스팅은 한 입 크기로 잘라먹는 타입스크립트 강의를 참고하여 작성했습니다. 문제가 될 소지가 있다면 댓글로 알려주세요. 바로 수정하겠습니다.

유틸리티 타입

유틸리티 타입 이란, 제네릭, 맵드 타입, 조건부 타입 등의 타입 조작 기능을 이용해 자주 사용되는 타입변환 패턴을 미리 정의해 놓은 타입을 말한다.

자주 유틸리티 타입을 살펴보고, 구현 코드도 이해해보자.

조건부 타입 기반

Exclude<T, U>

T에서 U를 제거하는 타입이다.

사용 예시

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

string | boolean 타입에서 boolean 을 제거하므로, A의 타입은 string이 된다.

구현 코드

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

T가 U를 확장하는 타입이라면 never 을, 아니라면 T 타입을 반환하도록 해서, U 타입만 제거해줄 수 있다.

Extract<T,U>

T에서 U를 추출하는 타입이다.

사용 예시

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

string | boolean 타입에서 boolean 을 추출하므로, B의 타입은 boolean이 된다.

구현 코드

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

T가 U를 확장하는 타입이라면 T 를, 아니라면 never 타입을 반환하도록 해서, T 타입만 추출해줄 수 있다.

ReturnType<T>

함수의 반환값 타입을 추출하는 타입이다.

사용 예시

function funcC() {
  return "hello";
}

type ReturnA = ReturnType<typeof funcC>;
// ReturnA : string

funcC 의 반환 값이 string 이고, 따라서 ReturnA 의 타입은 string이 된다.

구현 코드

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

T extends (...args: any) => anyT어떤 함수 타입 이어야 한다는 제약 조건을 명시한다.

T extends (...args: any) => infer R ? R : never;
T가 함수 타입이라면 반환 타입을 R로 추론하고, 그렇지 않으면 never를 반환한다는 의미이다.

맵드 타입 기반

아래 Post 타입을 기반으로 이해해보자.

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

Partial<T>

특정 객체 타입의 모든 프로퍼티를 선택적 프로퍼티로 바꿔주는 타입이다.

사용 예시

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

Posttags는 필수 프로퍼티였지만, 선택적 프로퍼티로 바뀌었기 때문에 생략해도 무방한 모습이다.

구현 코드

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

맵드 타입과 ? 키워드를 이용해 구현한다.

Required<T>

특정 객체 타입의 모든 프로퍼티를 필수 프로퍼티로 바꿔주는 타입이다.

사용 예시

const withThumbnailPost: Required<Post> = {
  title: "아무 post",
  tags: ["ts"],
  content: "",
  thumbnailURL: "https..",
};

thumbnailURL 은 선택적 프로퍼티였지만, 해당 유틸리티 타입을 사용해 필수 프로퍼티가 되었다.

구현 코드

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

선택적 프로퍼티를 나타내는?제거한다(-) 라는 의미로, -? 를 사용해 구현한다.

Readonly<T>

특정 객체 타입의 모든 프로퍼티를 읽기 전용 프로퍼티로 만들어주는 타입이다.

사용 예시

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

readonlyPost.content = ""; // error!!

모든 속성이 읽기 전용으로 지정되어, 수정을 하려고 하면 에러가 발생한다.

구현 코드

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

맵드 타입과 readonly 키워드를 이용해 구현한다.

Pick<T, K>

객체 타입 T로부터 지정한 프로퍼티 K 만 추출하는 타입이다.

사용 예시

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

Post 타입에서 titlecontent 타입만 추출한 모습이다.

구현 코드

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

K extends keyof T : K는 반드시 T의 키의 일부여야 한다는 제약조건

Omit<T, K>

객체 타입 T 에서 지정한 프로퍼티 K를 제외하는 타입이다.

사용 예시

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

Post 타입에서 title 속성만 제외한 모습이다.

구현 코드

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

K extends keyof T : K는 반드시 T의 키의 일부여야 한다는 제약조건
Exclude<keyof T, K> : T의 키 중에서 K를 제외한 키만 추출
Pick<T, Exclude<keyof T, K>> : T 에서 K를 제외한 키들만 골라 새로운 타입으로 구성

Record<K, V>

동일한 패턴을 갖는 객체 타입을 정의하는 타입이다.

사용 예시

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

구현 코드

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

K extends keyof any : K가 어떤 객체의 키 타입임을 명시한다.

profile
꾸준함을 목표로 합니다.

0개의 댓글