Utility Type 유틸리티 타입이란?

유틸리티 타입이란?

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://velog.io/@ggong/Typescript%EC%9D%98-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85-1-Record-Extract-Pick

https://kyounghwan01.github.io/blog/TS/fundamentals/utility-types/#partial

https://80000coding.oopy.io/edb8d09e-7ef9-4de2-9cf3-3b2d61d0c3e7

Leave a Comment