유틸리티 타입이란?
TypeScript에는 다양한 타입들이 존재합니다. 그 중에서 이번에는 유틸리티 타입에 대해서 알아보도록 하겠습니다. 유틸리티 타입은 제네릭 타입이라고도 불립니다. 이 타입은 잘 쓰면 쓸수록 코드를 짧고 간결하게 작성할 수 있다.
Record<K, T> 타입
Record 타입은 두 개의 타입을 받을 수 있습니다. 첫 번째 타입은 Key 값의 타입이고, 두 번째 제네릭 타입은 Key 값의 타입으로 갖는 타입을 반환합니다.
type Record<K, T> = {
[P in K]: T;
}
위 코드는 기본적인 Record 기본 형식의 코드입니다.
아래는 예시 코드입니다.
type ITypeField = {
title: string;
value: string;
}
type IFormType = 'text' | 'media';
const x: Record<IFormType, ITypeField> = {
text: {
title: 'article',
value: 'blog text',
},
media: {
title: 'media',
value: 'image, video text',
}
}
Record Type의 가장 큰 특징은 Key 값을 문자열 리터럴을 허용한다는 점입니다. 속성을 제한할 때 문자열 리터럴을 통해 Key 값에 호용 가능한 값을 제한합니다. 이를 통해 사용하는 개발자 입장에서 쉽게 타입 추론 및 사용 값을 파악할 수 있습니다.
또한 열거형을 Key 값으로 사용할 수도 있습니다.
type name = 'Kelly' | 'Kim' | 'Mary'; // 문자열 리터럴
enum names {
'Kelly' = 1,
'Kim' = 2,
'Mary' = 3,
} // 열거형
type info = Record<name, number>;
type infos = Record<names, number>;
keyof와 Record Type을 함께 사용하는 방법
keyof 키워드는 타입 또는 인터페이스에 존재하는 모든 키 값을 union 형태로 가져옵니다.
type keyofType = {
t: string,
s: number,
}
type Key = keyof KeyofType; // 여기에 들어갈 타입은 t | s 입니다.
type recordTypeByKeyof = Record<keyof keyofType, string>;
const useVar: recordTypeByKeyof = {
t: '텍스트',
s: '0',
}
💡 keyof 타입 연산자는 객체 타입에서 객체의 키 값들을 숫자나 문자열 리터럴 유니온을 생성합니다.
이처럼 기존 타입의 속성 이름을 Record Type의 Key 타입으로 하고 싶은 경우 keyof를 사용할 수 있습니다. 즉, 기존에 선언된 type, enum 값을 기반으로 예측 가능한 규칙 안에서 사용가능한 변수를 생성 후 만들 수 있습니다.
Exclude<T, U>, Extract<T, U>
Exclude 타입과 Extract은 2개의 제네릭 타입을 받을 수 있습니다.
Exclude<T, U> 타입은 T가 가진 타입 중 U와 겹치는 타입을 제외한 타입을 반환합니다.
Extract<T, U> 타입은 T에 대해 U가 가진 타입 중 할당 가능한 타입을 할당합니다. Exclude와는 반대되는 개념입니다.
type Exclude<T, U> = T extends U ? never : T;
type Extract<T, U> = T extends U ? T : never;
하나씩 보자면 Exclude는 T 타입이 U 타입을 상속하거나 동일 타입이라면 never(무시) 타입을 반환하고 아닐 경우 타입 값을 반환 합니다.
Extract는 T 타입에서 U 타입과 겹치는 타입만 추출해서 반환합니다. 겹치는 부분이 없다면 never 타입을 반환합니다.
var IWantNumber = Exclude<number|string, string>; // number 타입 반환
var IWantString = Extract<number|string, string>; // string 타입 반환
위 유틸리티 타입은 기존에 존재하는 타입을 활용해 새로운 타입에서 사용할 때 많이 활용합니다.
Partial<T>, Pick<T, K>
Partial 타입은 특정 타입의 부분 집합을 만족하는 타입을 정의할 수 있습니다.
Pick 타입은 특정 타입에서 몇 개의 속성을 선택하여 타입을 정의합니다.
interface Address {
email: string;
address1: string;
address2: string;
address3: string;
}
type MyAddress = Partial<Address>; // Partial 타입
const centbin: MyAddress = { email: 'centbin@naver.com' };
type OtherAddress = Pick<Address, 'email' | 'address3'>; // Pick 타입
const bincent: OtherAddress = { email: 'bincent@naver.com', address3: 'info' }
Partial 타입은 T 타입이 가지고 있는 속성들을 전체 혹은 부분적으로 사용할 수 있습니다.
Pick 타입은 T 타입이 가진 속성들 중 K 타입에 속한 속성들만 사용할 수 있습니다.
Partial 타입은 이미 선언된 타입 중 부분적으로 사용할 때 사용합니다.
Pick 타입은 이미 선언된 타입 중 특정 속성만 사용할 때 사용합니다.
이 타입을 현업에서 사용했을 때, 조심해야 할 점은 기반(Base)이 되는 타입을 수정하게 되면, 많은 타입들이 영향을 받을 수 있습니다. 상속과 비슷한 기능을 제공하기 때문에 조심히 사용해야 합니다.
대신 중복으로 선언될 타입을 줄일 수 있기 때문에 적절한 상황에서 사용하면 좋습니다.
Omit<T, K>
Omit은 전달받은 Type에서 문자열 리터럴 혹은 union of string으로 받은 키값들을 제외한 타입을 생성합니다.
interface Todo {
title: string;
name: string;
date: Datetime;
completed: boolean;
}
type TodoInfo = Omit<Todo, 'date' | 'name'>;
const info: TodoInfo = {
title: 'study english',
compelted: true,
}
console.log(info);
앞서 다룬 Pick 타입과 반대되는 타입입니다. 필요 없는 타입을 제외시키고, 나머지를 사용할 수 있기 때문에 타입을 제한할 수 있습니다.
정리
유틸리티 타입은 다양한 타입을 선언할 때, 최대한 재사용성을 올리기 위해 사용합니다. 특정 타입의 속성을 가져오거나 제외함으로써 TypeScript의 타입을 잘 활용할 수 있도록 도와줍니다. 그렇기 때문에 유틸리티 타입이라는 이름을 가지고 있습니다.
함께 보면 좋은 글
https://www.centbin.com/class-transformer%eb%9e%80-expose-exclude-type/
참고자료
https://kyounghwan01.github.io/blog/TS/fundamentals/utility-types/#partial
https://80000coding.oopy.io/edb8d09e-7ef9-4de2-9cf3-3b2d61d0c3e7