유틸리티 타입이란
interface Person {
name : string;
age : number;
}
const person : Readonly<Person> ={
name : "홍길동",
age : 29
}
person.name = ''
// ❌ name은 Readonly 프로퍼티입니다.
또는 다음과 같이 Partial<T> 유틸리티 타입을 이용해 특정 객체 타입의 모든 프로퍼티를 선택적 프로퍼티로 변환하는 것도 가능합니다.
interface Person {
name: string;
age: number;
}
const person: Partial<Person> = {
name: "홍길동",
};
타입스크립트는 굉장히 다양한 유틸리티 타입을 제공합니다. 아래의 타입스크립트 공식문서 에서 다양한 유틸리티 타입들을 확인할 수 있습니다.
Documentation - Utility Types
Types which are globally included in TypeScript
www.typescriptlang.org
이 중 맵드 타입을 기반과 조건부 타입 기반의 유틸리티 타입을 살펴보고자 합니다.

Partial<T>
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
다음으로 임시 저장 기능이 필요하다고 가정하겠습니다. 그럼 다음과 같이 임시 저장된 게시글을 변수로 저장할 수 있어야 합니다.
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
const draft: Post = { // ❌ tags 프로퍼티가 없음
title: "제목은 나중에 짓자...",
content: "초안...",
};
Partial<T> 타입으로 문제 해결하기
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
const draft: Partial<Post> = {
title: "제목 나중에 짓자",
content: "초안...",
};
Partial<T> 구현하기
type Partial<T> = any;
다음으로는 T에 할당된 객체 타입의 모든 프로퍼티를 선택적 프로퍼티로 바꿔줘야 합니다. 기존 객체 타입을 다른 타입으로 변환하는 타입은 맵드 타입이었습니다. 따라서 맵드 타입을 이용해 다음과 같이 수정합니다.
type Partial<T> = {
[key in keyof T]?: T[key];
};
Required<T>
앞서 살펴본 예제에 이어서 이번에는 썸네일이 반드시 있어야 하는 게시글이 하나 필요하다고 가정하겠습니다.
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
(...)
// 반드시 썸네일 프로퍼티가 존재해야 하는 게시글
const withThumbnailPost: Post = {
title: "한입 타스 후기",
tags: ["ts"],
content: "",
thumbnailURL: "https://...",
};
withThumbnailPost는 모종의 이유(마케팅 등)로 반드시 썸네일이 포함된 게시글이어야 합니다. 그런데 Post 타입의 thumbnailURL 프로퍼티가 현재 선택적 프로퍼티로 설정되어 있기 때문에 다음과 같이 실수로 주석 처리하거나 삭제한다고 해도 타입 오류가 발생하지는 않습니다.
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
(...)
const withThumbnailPost: Post = {
title: "한입 타스 후기",
tags: ["ts"],
content: "",
// thumbnailURL: "https://...",
};
우리가 원하는 것은 이 변수에 한정해 thumbnailURL을 필수 프로퍼티로 만들어 주고 싶습니다. 어떻게 할 수 있을까요?
Required<T> 타입으로 문제 해결하기
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
(...)
const withThumbnailPost: Required<Post> = { // ❌
title: "한입 타스 후기",
tags: ["ts"],
content: "",
// thumbnailURL: "https://...",
};
Required<T> 타입 구현하기
type Required<T> = {
[key in keyof T]: T[key];
};
그리고 나서 이제 모든 프로퍼티가 필수 프로퍼티가 되도록 만들어야 합니다. 모든 프로퍼티를 필수 프로퍼티로 만든다는 말은 반대로 바꿔보면 모든 프로퍼티에서 ‘선택적’ 이라는 기능을 제거하는 것 과 같습니다. 따라서 다음과 같이 -?를 프로퍼티 이름 뒤에 붙여주면 됩니다.
type Required<T> = {
[key in keyof T]-?: T[key];
};
Readonly
이번에는 앞서 만들던 예제에 이어서 절대 내부를 수정할 수 없는 보호된 게시글이 하나 필요하다고 가정하겠습니다.
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
(...)
const readonlyPost: Post = {
title: "보호된 게시글입니다.",
tags: [],
content: "",
};
readonlyPost.content = '해킹당함';
변수 readonlyPost는 보호받아야 하는 게시글로 절대 객체 내부의 값을 수정하지 못하게 막아야 한다고 가정했습니다. 그러나 Post 타입의 모든 프로퍼티가 다 readonly 설정이 안되어 있기 때문에 지금은 수정을 방지하지 못합니다
Readonly<T>로 문제 해결하기
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
(...)
const readonlyPost: Readonly<Post> = {
title: "보호된 게시글입니다.",
tags: [],
content: "",
};
readonlyPost.content = '해킹당함'; // ❌
Readonly<T> 구현하기
type Readonly<T> = {
readonly [key in keyof T]: T[key];
};
Pick<T, K>
이번에는 이전 시간에 만들던 예제에 이어서 다음과 같이 옛날에 작성된 포스트가 하나 존재한다고 가정하겠습니다.
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
(...)
const legacyPost: Post = { // ❌
title: "",
content: "",
};
Pick<T, K>로 문제 해결하기
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
(...)
const legacyPost: Pick<Post, "title" | "content"> = {
title: "",
content: "",
};
// 추출된 타입 : { title : string; content : string }
Pick<T, K> 타입 구현하기
type Pick<T, K> = any;
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<T, K>
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
(...)
const noTitlePost: Post = { // ❌
content: "",
tags: [],
thumbnailURL: "",
};
Omit<T, K>로 문제 해결하기
const noTitlePost: Omit<Post, "title"> = {
content: "",
tags: [],
thumbnailURL: "",
};
Omit<T, K> 구현하기
type Omit<T, K> = any;
type Omit<T, K extends keyof T> = any;
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
Record<K, V>
type Thumbnail = {
large: {
url: string;
};
medium: {
url: string;
};
small: {
url: string;
};
};
그런데 여기에 watch 버전이 또 추가되어야 한다고 가정합니다. 그럼 다음과 같이 똑같이 생긴 프로퍼티를 하나 더 추가해줘야 합니다. 앞으로 버전이 많아질 수록 계속해서 중복코드가 발생하게 될 겁니다.
type Thumbnail = {
(...)
watch: {
url: string;
};
};
Record<K, V> 타입으로 문제 해결하기
type Thumbnail = Record<
"large" | "medium" | "small",
{ url: string }
>;
Record<K, V> 구현하기
type Record<K extends keyof any, V> = {
[key in K]: V;
};
'TypeScript' 카테고리의 다른 글
TypeScript : 타입스크립트와 리액트 (0) | 2024.08.13 |
---|---|
TypeScript : 유틸리티 타입(2) (0) | 2024.08.12 |
TypeScript : infer (0) | 2024.08.12 |
TypeScript : 분산적인 조건부 타입 (0) | 2024.08.12 |
TypeScript : 조건부 타입 (0) | 2024.08.12 |
유틸리티 타입이란
interface Person {
name : string;
age : number;
}
const person : Readonly<Person> ={
name : "홍길동",
age : 29
}
person.name = ''
// ❌ name은 Readonly 프로퍼티입니다.
또는 다음과 같이 Partial<T> 유틸리티 타입을 이용해 특정 객체 타입의 모든 프로퍼티를 선택적 프로퍼티로 변환하는 것도 가능합니다.
interface Person {
name: string;
age: number;
}
const person: Partial<Person> = {
name: "홍길동",
};
타입스크립트는 굉장히 다양한 유틸리티 타입을 제공합니다. 아래의 타입스크립트 공식문서 에서 다양한 유틸리티 타입들을 확인할 수 있습니다.
Documentation - Utility Types
Types which are globally included in TypeScript
www.typescriptlang.org
이 중 맵드 타입을 기반과 조건부 타입 기반의 유틸리티 타입을 살펴보고자 합니다.

Partial<T>
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
다음으로 임시 저장 기능이 필요하다고 가정하겠습니다. 그럼 다음과 같이 임시 저장된 게시글을 변수로 저장할 수 있어야 합니다.
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
const draft: Post = { // ❌ tags 프로퍼티가 없음
title: "제목은 나중에 짓자...",
content: "초안...",
};
Partial<T> 타입으로 문제 해결하기
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
const draft: Partial<Post> = {
title: "제목 나중에 짓자",
content: "초안...",
};
Partial<T> 구현하기
type Partial<T> = any;
다음으로는 T에 할당된 객체 타입의 모든 프로퍼티를 선택적 프로퍼티로 바꿔줘야 합니다. 기존 객체 타입을 다른 타입으로 변환하는 타입은 맵드 타입이었습니다. 따라서 맵드 타입을 이용해 다음과 같이 수정합니다.
type Partial<T> = {
[key in keyof T]?: T[key];
};
Required<T>
앞서 살펴본 예제에 이어서 이번에는 썸네일이 반드시 있어야 하는 게시글이 하나 필요하다고 가정하겠습니다.
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
(...)
// 반드시 썸네일 프로퍼티가 존재해야 하는 게시글
const withThumbnailPost: Post = {
title: "한입 타스 후기",
tags: ["ts"],
content: "",
thumbnailURL: "https://...",
};
withThumbnailPost는 모종의 이유(마케팅 등)로 반드시 썸네일이 포함된 게시글이어야 합니다. 그런데 Post 타입의 thumbnailURL 프로퍼티가 현재 선택적 프로퍼티로 설정되어 있기 때문에 다음과 같이 실수로 주석 처리하거나 삭제한다고 해도 타입 오류가 발생하지는 않습니다.
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
(...)
const withThumbnailPost: Post = {
title: "한입 타스 후기",
tags: ["ts"],
content: "",
// thumbnailURL: "https://...",
};
우리가 원하는 것은 이 변수에 한정해 thumbnailURL을 필수 프로퍼티로 만들어 주고 싶습니다. 어떻게 할 수 있을까요?
Required<T> 타입으로 문제 해결하기
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
(...)
const withThumbnailPost: Required<Post> = { // ❌
title: "한입 타스 후기",
tags: ["ts"],
content: "",
// thumbnailURL: "https://...",
};
Required<T> 타입 구현하기
type Required<T> = {
[key in keyof T]: T[key];
};
그리고 나서 이제 모든 프로퍼티가 필수 프로퍼티가 되도록 만들어야 합니다. 모든 프로퍼티를 필수 프로퍼티로 만든다는 말은 반대로 바꿔보면 모든 프로퍼티에서 ‘선택적’ 이라는 기능을 제거하는 것 과 같습니다. 따라서 다음과 같이 -?를 프로퍼티 이름 뒤에 붙여주면 됩니다.
type Required<T> = {
[key in keyof T]-?: T[key];
};
Readonly
이번에는 앞서 만들던 예제에 이어서 절대 내부를 수정할 수 없는 보호된 게시글이 하나 필요하다고 가정하겠습니다.
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
(...)
const readonlyPost: Post = {
title: "보호된 게시글입니다.",
tags: [],
content: "",
};
readonlyPost.content = '해킹당함';
변수 readonlyPost는 보호받아야 하는 게시글로 절대 객체 내부의 값을 수정하지 못하게 막아야 한다고 가정했습니다. 그러나 Post 타입의 모든 프로퍼티가 다 readonly 설정이 안되어 있기 때문에 지금은 수정을 방지하지 못합니다
Readonly<T>로 문제 해결하기
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
(...)
const readonlyPost: Readonly<Post> = {
title: "보호된 게시글입니다.",
tags: [],
content: "",
};
readonlyPost.content = '해킹당함'; // ❌
Readonly<T> 구현하기
type Readonly<T> = {
readonly [key in keyof T]: T[key];
};
Pick<T, K>
이번에는 이전 시간에 만들던 예제에 이어서 다음과 같이 옛날에 작성된 포스트가 하나 존재한다고 가정하겠습니다.
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
(...)
const legacyPost: Post = { // ❌
title: "",
content: "",
};
Pick<T, K>로 문제 해결하기
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
(...)
const legacyPost: Pick<Post, "title" | "content"> = {
title: "",
content: "",
};
// 추출된 타입 : { title : string; content : string }
Pick<T, K> 타입 구현하기
type Pick<T, K> = any;
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<T, K>
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
(...)
const noTitlePost: Post = { // ❌
content: "",
tags: [],
thumbnailURL: "",
};
Omit<T, K>로 문제 해결하기
const noTitlePost: Omit<Post, "title"> = {
content: "",
tags: [],
thumbnailURL: "",
};
Omit<T, K> 구현하기
type Omit<T, K> = any;
type Omit<T, K extends keyof T> = any;
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
Record<K, V>
type Thumbnail = {
large: {
url: string;
};
medium: {
url: string;
};
small: {
url: string;
};
};
그런데 여기에 watch 버전이 또 추가되어야 한다고 가정합니다. 그럼 다음과 같이 똑같이 생긴 프로퍼티를 하나 더 추가해줘야 합니다. 앞으로 버전이 많아질 수록 계속해서 중복코드가 발생하게 될 겁니다.
type Thumbnail = {
(...)
watch: {
url: string;
};
};
Record<K, V> 타입으로 문제 해결하기
type Thumbnail = Record<
"large" | "medium" | "small",
{ url: string }
>;
Record<K, V> 구현하기
type Record<K extends keyof any, V> = {
[key in K]: V;
};
'TypeScript' 카테고리의 다른 글
TypeScript : 타입스크립트와 리액트 (0) | 2024.08.13 |
---|---|
TypeScript : 유틸리티 타입(2) (0) | 2024.08.12 |
TypeScript : infer (0) | 2024.08.12 |
TypeScript : 분산적인 조건부 타입 (0) | 2024.08.12 |
TypeScript : 조건부 타입 (0) | 2024.08.12 |