TS에는 타입 적용에 편리한 기능을 미리 정의해논 '유틸리티 타입' 이란게 있다.
이걸 잘 쓰면 부분 추출, 부분 제거, 전체 속성 변경 등 손가락을 덜 혹사시키면서 코딩이 가능하다.
잘 써먹을 수 있도록 유틸리티 타입을 공부 및 모사해보자 👊
타입스크립트에서 만들어 논 타입 변환을 용이하게 도와주는 도구!
유틸리티 타입은 어떤게 있을까?
Partial<T>, Required<T>, ReadOnly<T>, ...
공식문서에서 안내되고 있는 건 총 20개다.
많긴한데 Partial부터 차근차근 분석해보자!
참고: 유틸리티 타입 - 공식문서
타입 T의 모든 속성을 Optional 하게 만드는 타입이다.
그래서 T의 일부는 있고 일부는 없어도 되는 타입을 만든다.
타입스크립트에 이미 정의된 코드를 살펴보자.
type Partial<T> = {
[P in keyof T]?: T[P];
}
keyof, in, ? 등 알듯말듯한데, 코드는 생각보다 간단하다.
알듯말듯한 구성요소들을 알아보자.
오브젝트 타입의 키를 유니온 타입으로 뽑아내는 연산자
참고: 유니온 타입 - 공식문서
참고: keyof 연산자 - 공식문서
자바스크립트의 for in 문법과 유사하게 동작하는 문법
반복문을 이용하여 타입을 선언할 때 사용된다.
유니온 타입인 B의 요소들이 하나씩 A에 반영되며 작업을 반복하여 처리하는 방식이다.
Indexed Access Type으로 오브젝트내 속성의 타입을 뽑아낼때 쓸 수 있다.
타입 T의 P 속성에게 설정된 타입을 T[P]로 표현한다.
오브젝트의 특정 속성이 있을 수도 있고 없을 수도 있는 경우 Optional 이라고 말하며, ? 키워드로 표현 가능하다.
참고: Optional - 공식문서
각 요소에 대해 간단하게 정리해봤다.
이를 바탕으로 Partial을 한 문장으로 나타내보자.
💡 타입 T의 모든 키를 순회하면서 해당 키의 타입에 Optional을 추가한다.
그리고 이 문장으로부터 Partial<T>를 모사해보자.
우선, 괜히 어렵게 만들 제네릭은 뒤로하고 Person
이라는 타입으로 시작한다.
유틸리티 타입 Partial<T>
을 사용한 Partial<Person>
의 결과와 동일한 PartialPerson
타입을 만들어보자.
사용할 타입과 원하는 결과
Person 타입
type Person = {
name: string;
age: number;
height: number;
weight: number;
}
Partial<Person>
진행 과정
1. Person
의 키 목록을 가져오고
2. 키 목록을 순회하면서
3. 각 키의 원래 타입을 적용하고
4. Optinal도 또한 적용한다.
Person
의 키 목록 가져오기keyof를 사용하여 키 목록을 가져와보자.
아래 사진처럼 키들이 유니온 타입으로 묶여나온다.
[A in B]의 동작 방식을 익힐 겸 1번의 결과( PersonKeyOf
)를 이용하여 MappedPerson
이라는 새로운 타입을 만들어보자.
MappedPerson
은 모든 키의 타입이 string
을 가진다.
type MappedPerson = {
[P in PersonKeyOf]: string;
}
keyof + [A in B] 를 이용해서 위처럼 코딩이 가능한데, 결과 사진을 보기전에 어떻게 처리가 될 지 상상해보자.
P가 가지게 되는 값들은 PersonKeyOf('name', 'age', 'height', 'weight') 이다.
이 4가지 값들로 반복문이 돈다고 생각하면,
P가 name 인 경우, name: string;
P가 age 인 경우, age: string;
P가 height 인 경우, height: string;
P가 weight 인 경우, weight: string;
이런 식의 흐름이 되어 위 4가지가 모인 새로운 타입 MappedPerson
이 만들어진다.
MappedPerson
MappedPerson
의 string
이 아닌 원래 타입을 가져오기 위해서 T[P]을 사용하자.
우리 상황에선 T
에 해당하는 값은 Person
이다.
그렇게 만든 새로운 타입을 IndexedAccessPerson
이라 하자.
type IndexedAccessPerson = {
[P in PersonKeyOf]: Person[P];
}
동장 방식은 당연히 2번과정과 동일하다. 대신 타입이 다르다.
P가 name 인 경우,
name: Person[name];
→ name: string;
P가 age 인 경우,
age: Person[age];
→ age: number;
P가 height 인 경우,
height: Person[height];
→ height: number;
P가 weight 인 경우,
weight: Person[weight];
→ weight: number;
최종적으로 적용된 4가지가 모여서 IndexedAccessPerson
이 만들어진다.
IndexedAccessPerson
? 키워드를 :
앞에 붙여주면 최종 타입 PartialPerson
이 만들어진다.
type PartialPerson = {
[P in PersonKeyOf]?: Person[P];
}
과정은 2,3 번을 통해 충분한 연습이 됬다 생각하니 바로 결과 사진을 보자.
PartialPerson
type PartialPersonV1 = {
[P in PersonKeyOf]?: Person[P];
}
// 1. PersonKeyOf -> keyof Person
type PartialPersonV2 = {
[P in keyof Person]?: Person[P];
}
// 2. Person -> Generic
type PartialPerson<T> = {
[P in keyof T]?: T[P];
}
// Original Patrial
type Partial<T> = {
[P in keyof T]?: T[P];
};
여기까지 기존 타입에 optional 속성을 추가하는 Partial에 대해 공부했다.
바탕이 되는 개념인 keyof, [A in B], Indexed Access Type, Optional에 대해서도 알 수 있었고, Partial 모사를 통해 독립적이였던 개념들을 연결시킬 수 있었다.
공부하면서 작성한 코드입니다.
Optional 모사하기
모르고 있던게 많아 정리하면서 진행하느라 분량이 길어졌네요.
나머지 타입들은 다음 포스팅으로 넘기겠습니다.
공부한 내용을 공유하는 글이니 제가 잘못된 정보를 전달할 수도 있습니다.
그럴 땐 거침없이 태클걸어주세요.
오 Partial 정의 부터 파고드는 글이라 그런지 정말 유익했습니다 !