티스토리 뷰
🐰 유틸리티 타입
제네릭, 맵드 타입, 조건부 타입 등의 타입 조작 기능을 이용해 실무에서 자주 사용되는 타입을 미리 만들어 놓은 것
🐰 맵드 타입 기반의 유틸리티 타입
1. Partial<T>
partial
부분적인, 일부분의
특정 객체 타입의 모든 프로퍼티를 선택적 프로퍼티로 변환하는 타입
interface Post {
title: string;
tag: string[];
content: string;
thumbnailURL?: string;
}
const draft: Partial<Post> = {
title: '제목 나중에 짓자',
content: '초안...',
}
타입 Post는 thumbnailURL 프로퍼티를 제외한 다른 프로퍼티는 선택적 프로퍼티가 아니다.
하지만 변수 draft는 Partial<T> 유틸리티 타입을 이용해 모든 프로퍼티를 선택적 프로퍼티로 만들어 모든 프로퍼티를 가지고 있지 않아도 오류가 발생하지 않는다.
Partial<T> 직접 구현하기
// Partial 타입 직접 구현
type Partial<T> = {
// 맵드 타입 이용
// T의 모든 key 꺼내오기
[key in keyof T]? : T[key]; // ?를 붙여 선택적 프로퍼티로 만들기
};
- 하나의 타입 변수 T를 사용하는 제네릭 타입을 만듬
- 맵드 타입을 이용해 기존의 객체 타입을 다른 타입으로 변환
- key in keyof T 를 이용해 T의 프로퍼티를 하나씩 꺼내오기
- 선택적 프로퍼티로 만들기
2. Required<T>
required
필수의, 필수적인
특정 객체 타입의 모든 프로퍼티를 필수(선택적이지 않은) 프로퍼티로 바꿔주는 타입
interface Post {
title: string;
tag: string[];
content: string;
thumbnailURL?: string;
}
const withThumbnailPost: Required<Post> = {
title: '한입 타스 후기',
tag: ['ts'],
content: '',
// 선택적 프로퍼티인 thumbnailURL도 필수 프로퍼티가 됨
// thumbnailURL: 'https://...', // -> ❌
}
thumbnailURL은 선택적 프로퍼티로 정의가 되어있지만 필수 프로퍼티로 사용하고자 할 때는 Required<T> 유틸리티 타입을 이용한다.
Required<Post>는 Post 타입의 모든 프로퍼티가 필수 프로퍼티로 변환된 객체이므로 thumbnailURL 프로퍼티를 생략하게 되면 오류가 발생한다.
Required<T> 타입 직접 구현하기
// Required 직접 구현
type Required<T> = {
// 맵드 타입을 이용해 프로퍼티 가져오기
// 모든 프로퍼티에서 '선택적'이라는 기능을 빼주기
[key in keyof T]-?: T[key];
}
- 하나의 타입 변수 T를 사용하는 제네릭 타입을 만듬
- 맵드 타입을 이용해 기존의 객체 타입을 다른 타입으로 변환
- key in keyof T 를 이용해 T의 프로퍼티를 하나씩 꺼내오기
- 모든 프로퍼티를 필수 프로퍼티로 만들기
- 모든 프로퍼티에서 '선택적'이라는 기능을 제거
- => -? 를 프로퍼티 이름 뒤에 붙임
-?
? 가 붙어있는 선택적 프로퍼티가 있으면 ? 를 제거하라는 의미
3. Readonly<T>
readonly
읽기 전용
특정 객체 타입의 모든 프로퍼티를 읽기 전용 프로퍼티로 바꿔주는 타입
interface Post {
title: string;
tag: string[];
content: string;
thumbnailURL?: string;
}
const readonlyPost: Readonly<Post> = {
title: '보호된 게시글 입니다.',
tag: [],
content: '',
};
// 프로퍼티 수정 불가능
readonlyPost.content = '본문'; // ❌
Readonly<T> 유틸리티 타입은 T 타입의 모든 프로퍼티를 readonly(읽기 전용) 프로퍼티로 변환한다.
따라서 점표기법을 이용해 특정 프로퍼티의 값을 수정하려고 하면 오류가 발생한다.
Readonly<T> 타입 직접 구현하기
// Readonly 직접 구현
type Readonly<T> = {
// 맵드 타입으로 객체의 프로퍼티 가져오기
// readonly를 앞에 붙여 읽기 전용으로 만들기
readonly[key in keyof T]: T[key];
}
- 하나의 타입 변수 T를 사용하는 제네릭 타입을 만듬
- 맵드 타입을 이용해 기존의 객체 타입을 다른 타입으로 변환
- key in keyof T 를 이용해 T의 프로퍼티를 하나씩 꺼내오기
- 모든 프로퍼티를 readonly(읽기 전용) 프로퍼티로 만들기
- 프로퍼티 이름 앞에 readonly를 붙임
4. Pick<T, K>
객체 타입으로부터 특정 프로퍼티만 골라내는 타입
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
const legacyPost: Pick<Post, 'title' | 'content'> = {
title: '옛날 글',
content: '옛날 컨텐츠',
}
Pick<T, K> 유틸리티 타입은 특정 객체 타입을 타입 변수 T에 받고 이 객체에 존재하는 특정 프로퍼티를 타입 변수 K에 받아 특정 프로퍼티 만을 골라낸다.
따라서 변수 legacyPost는 Post 타입으로부터 'title'과 'content' 프로퍼티만 가져온 객체 타입이 된다.
Pick<T, K> 타입 직접 구현하기
// Pick<T, K> 직접 만들기
type Pick<T, K extends keyof T> = {
// 타입 변수 K가 T의 key로만 이루어진 string literal 유니온 타입임을 보장해야 함
// K extends 'title' | 'tags' | 'content' | 'thumbnailURL'
// 'title' | 'content' extends 'title' | 'tags' | 'content' | 'thumbnailURL'
[key in K]: T[key];
}
- 두개의 타입 변수 T, K를 사용하는 제네릭 타입을 만듬
- 맵드 타입을 이용해 기존의 객체 타입을 다른 타입으로 변환
- key in K 를 이용해 특정 프로퍼티를 하나씩 꺼내오기
- K가 T의 key로만 이루어진 string literal 유니온 타입임을 보장하기
- K extends keyof T 로 제약하기
5. Omit<T, K>
omit
생략하다, 빼다
객체 타입으로부터 특정 프로퍼티를 제거하는 타입
interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
const noTitlePost: Omit<Post, 'title'> = {
// Post 타입의 title 프로퍼티만 제거
content: '',
tags: [],
thumbnailURL: '',
};
Omit<T, K> 유틸리티 타입은 특정 객체 타입을 타입 변수 T에 받아 타입 변수 K에 받아온 프로퍼티를 제거한다.
변수 noTitlePost는 Omit을 이용해 Post 타입으로부터 title 프로퍼티를 제거한 타입으로 변수의 타입을 정의한다.
Omit<T, K> 직접 구현하기
// Omit 직접 구현
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'>
- 두개의 타입 변수 T, K를 사용하는 제네릭 타입을 만듬
- K에 제약을 추가
- K extends keyof T로 K가 T의 key로만 이루어진 string literal 유니온 타입임을 보장
- Pick 타입을 이용해 완성
- keyof T는 'title' | 'content' | 'tags' | 'thumbnailURL'
- => Pick<T, Exclude<keyof T, K>>은 Pick<Post, Exclude<'title' | 'content' | 'tags' | 'thumbnailURL' , 'title>>
- 이를 한번 더 변환하면 Pick<Post, 'content' | 'tags' | 'thumbnailURL'>
6. Record<K, V>
새로운 객체 타입을 정의할 때 인덱스 시그니쳐처럼 유연하지만 조금 더 제한적인 객체 타입을 정의하는 타입
(실무에서 자주 사용하는 타입)
type ThumnailLegacy = {
large: {
url: string;
};
medium: {
url: string;
};
small: {
url: string;
};
};
// 타입 ThumbnailLegacy에 버젼이 계속 추가되고 똑같이 생긴 프로퍼티를 추가해야하는 경우
// Record 타입으로 객체 타입 정의
type Thumbnail = Record<'large' | 'medium' | 'small' , {url: string}>;
// 첫번째 타입 변수 => 객체의 프로퍼티 키
// 두번째 타입 변수 => 키의 value 타입
// type Thumbnail = {
// large: {
// url: string;
// };
// medium: {
// url: string;
// };
// small: {
// url: string;
// };
//}
Record<K, V> 유틸리티 타입은 첫번째 타입 변수로는 객체의 프로퍼티 키를 받아오고 두번째 타입 변수로는 각 프로퍼티 밸류를 받아 객체 타입을 정의한다.
타입 Thumbnail은 large, medium, small 프로퍼티가 있는 객체 타입이 된다.
Thumbnail에 프로퍼티를 추가하는 경우 첫번째 타입 변수 유니온에 추가로 작성한다. 또 버젼별로 동일한 새로운 프로퍼티를 추가하는 경우에는 두번째 타입 변수에 추가한다.
// 프로퍼티를 추가하는 경우 유니온에 추가 -> watch 프로퍼티 추가
// 버전별로 새로운 프로퍼티를 추가하는 경우 두번째 타입 변수에 추가 -> size: number 추가
type Thumbnail = Record<'large' | 'medium' | 'small' | 'watch', {url: string; size: number}>;
type Thumbnail = {
large: {
url: string;
size: number;
};
medium: {
url: string;
size: number;
};
small: {
url: string;
size: number;
};
watch: {
url: string;
size: number;
};
}
Record<K, V> 직접 구현하기
// Record 구현
type Record<K extends keyof any, V> = { // K에 제약 걸기 -> K에 들어오는 타입은 객체 타입의 키를 추출한 유니온 타입
[key in K]: V
}
- 두개의 타입 변수 T, K를 사용하는 제네릭 타입을 만듬
- 맵드 타입을 이용해 기존의 객체 타입을 다른 타입으로 변환
- key in K 를 이용해 특정 프로퍼티를 하나씩 꺼내오기
- K가 T의 key로만 이루어진 string literal 유니온 타입임을 보장하기
- K extends keyof T 로 제약하기
'코딩 > 한 입 크기로 잘라먹는 타입스크립트' 카테고리의 다른 글
[TypeScript] infer (0) | 2023.09.25 |
---|---|
[TypeScript] 분산적인 조건부 타입 (0) | 2023.09.25 |
[TypeScript] 조건부 타입 (0) | 2023.09.25 |
[TypeScript] 타입 조작 - 템플릿 리터럴 타입 (0) | 2023.09.23 |
[TypeScript] 타입 조작 - 맵드 타입 (0) | 2023.09.22 |