유틸리티 타입(utility type)이란 타입스크립트가 자체적으로 제공하는 특수한 타입들입니다. 우리가 지금까지 배웠던 제네릭, 맵드 타입, 조건부 타입 등의 타입 조작 기능을 이용해 실무에서 자주 사용되는 유용한 타입들을 모아 놓은 것을 의미합니다.
예를 들어 다음과 같이 Readonly<T>
와 같은 유틸리티 타입을 이용해 특정 객체 타입의 모든 프로퍼티를 읽기 전용 프로퍼티로 변환할 수 있습니다.
interface Person {
name : string;
age : number;
}
const person : Readonly<Person> ={
name : "이정환",
age : 27
}
person.name = '' // ❌ No
다음과 같이 Partial<T>
유틸리티 타입을 이용해 특정 객체 타입의 모든 프로퍼티를 선택적 프로퍼티로 변환할 수 있습니다.
interface Person {
name: string;
age: number;
}
const person: Partial<Person> = {
name: "이정환",
};
타입스크립트는 굉장히 다양한 유틸리티 타입을 제공합니다.
참조: TypeScript: Documentation - Utility Types
Partial
은 타입스크립트의 유틸리티 타입 중 하나로, 객체의 모든 프로퍼티를 선택적으로 만듭니다. 즉, Partial<T>
는 T
의 모든 프로퍼티를 optional
하게 만드는 새로운 타입을 생성합니다. 이는 기존의 객체 타입에서 필요한 일부만 사용하거나, 일부 프로퍼티의 값만 초기화하고 싶을 때 유용하게 사용할 수 있습니다.
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
// Partial<T> 구현해보기
type Partial<T> = {
[key in keyof T]?: T[key];
};
const draft: Partial<Post> = {
title: "제목 나중에 짓자",
content: "초안...",
};
Required
는 모든 프로퍼티를 필수 프로퍼티로 변환합니다. 즉, 선택적 프로퍼티를 가진 타입에서 모든 프로퍼티를 필수적으로 제공하도록 강제하는 유틸리티 타입입니다.
// Required<T> 구현해보기
type Required<T> = {
[key in keyof T]-?: T[key];
};
const withThumbnailPost: Required<Post> = {
title: "한입 타스 후기",
tgas: ["ts"],
content: "",
thumbnailURL: "https://...",
};
Required
는 특정 객체 타입의 모든 프로퍼티를 읽기 전용 프로퍼티로 변환합니다.
// Readonly<T> 구현해보기
type Readonly<T> = {
readonly [key in keyof T]: T[key];
};
const readonlyPost: Readonly<Post> = {
title: "보호된 게시글 입니다.",
tags: [],
content: "",
};
readonlyPost.content = ""; // ❌ No
Pick<T, K>
는 T
타입의 프로퍼티 중 K
로 지정한 프로퍼티만을 포함한 새로운 타입을 만듭니다.
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
// Pick<T, K> 구현해보기
type Pick<T, K extends keyof T> = {
[key in K]: T[key];
};
const legacyPost: Pick<Post, "title" | "content"> = {
title: "엣날 글",
content: "옛날 컨텐츠",
};
Omit<T, K>
는 T
타입의 프로퍼티 중 K
로 지정한 프로퍼티를 제외한 새로운 타입을 만듭니다.
// Omit<T, K> 구현해보기
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
// T = Post, K = "title"
// Pick<Post, Exclude<keyof Post, "title">>
// Pick<Post, Exclude<"title" | "content" | "tags" | "thumbnailURL", "title">>
// Pick<Post, "content" | "tags" | "thumbnailURL">
const noTitlePost: Omit<Post, "title"> = {
content: "",
tags: [],
thumbnailURL: "",
};
Record<K, V>
유틸리티 타입은 K
로 지정한 프로퍼티 키들이 V
로 지정한 값을 가지는 객체 타입을 만듭니다.
type ThumbnailLegacy = { // 비효율적
large: {
url: string;
};
medium: {
url: string;
};
small: {
url: string;
};
watch: {
url: string;
}
}
// Record<K, V> 구현해보기
type Record<K extends keyof any, V> = {
[key in K]: V;
};
type Thumbnail = Record<
"large" | "medium" | "small",
{ url: string; size: number }
>;
Exclude<T, U>
는 타입 T
에서 U
를 제외한 나머지 타입을 반환합니다.
// Exclude<T, U> 구현해보기
type Exclude<T, U> = T extends U ? never : T;
// 1 단계
// Exclude<string, boolean> |
// Exclude<boolean, boolean>
// 2 단계
// string |
// never
// 최종적으로는
// string
type A = Exclude<"string" | "boolean", "string">; // A = boolean
Extract<T, U>
는 타입 T
에서 U
와 일치하는 타입만을 추출합니다.
// Extract<T, U> 구현해보기
type Extract<T, U> = T extends U ? T : never;
type B = Extract<string | boolean, boolean>; // B = string
ReturnType<T>
는 함수 T의 반환값의 타입을 추출합니다.
type ReturnType<T extends (...args: any) => any> = T extends (
...agrs: any
) => infer R
? R
: never;
function funcA() {
return "hello";
}
function funcB() {
return 10;
}
type ReturnA = ReturnType<typeof funcA>; // ReturnA = string
type ReturnB = ReturnType<typeof funcB>; // ReturnB = number
Reference