안녕하세요?
이번 시간에는 타입스크립트에서 자체적으로 제공하는 Utility Types에 대해 알아 보겠습니다.
https://www.typescriptlang.org/docs/handbook/utility-types.html
공식 홈페이지를 보면 Utility Types으로 제공하는게 아주 많은 데요.
우리는 가장 많이 쓰이는 몇가지에 대해 예를 들어 알아 보겠습니다.
1. Partial<Type>
먼저 Partial<Type>에 대해 알아 보겠습니다.
Partial<Type>을 언제 쓰이는지는 예를 들어 보면 쉽게 이해 할 수 있습니다.
다음과 같이 MyUser라는 인터페이스가 있다고 합시다.
interface MyUser {
name: string;
id: string;
email?: string;
}
name과 id는 옵셔널이 아니고 email만 옵셔널로 지정되어 있네요.
이와 같은 MyUser 인터페이스를 이용해서 MyUser를 편집할려고 할때가 있습니다.
예를 들어 email 부분을 수정할려고 한다면 어떻게 해야 할까요?
const merge = (user: MyUser, overrides: MyUserOptionals): MyUser => {
return {
...user,
...overrides
}
}
위와 같은 merge 함수를 보시면 기존 user 데이터와 overrides 데이터를 합치는 함수인데요.
overrides 데이터는 타입이 MyUserOptionals 입니다.
interface MyUserOptionals {
name?: string;
id?: string;
email?: string;
}
MyUserOptionals 인터페이스를 보면 모든 항목이 옵셔널로 되어 있습니다.
즉, 어떤 항목이든지 변경하려는 항목만 넣을 수 있다는 뜻입니다.
이제 merge 함수를 사용해 볼까요?
아래 코드를 보십시요.
console.log(merge({
name: "Ki-hun",
id: "456",
email: "kihun@squid.com"
}, {
email: "jungkihun@squid.com"
}))
첫번째 객체에다가 두번째 객체를 덮어 씌라는 얘기입니다.
그래서 MyUserOptions 인터페이스가 필요합니다.
실행 결과를 볼까요?
예상 되로 잘 실행 됐습니다.
Partial<Type> 을 설명할 필요없이 별다른 수고없이 실행도 잘되고 코드 이해도 쉬었습니다.
그런데 만약 MyUser가 phone 항목이 추가로 생겼다면 어떻게 할까요?
interface MyUser {
name: string;
id: string;
email?: string;
phone?: string;
}
그러면 MyUserOptionals 인터페이스도 바꿔줘야 합니다.
interface MyUserOptionals {
name?: string;
id?: string;
email?: string;
phone?: string;
}
뭔가 수작업이 계속 일어나는데요. 코딩할때 수작업은 힘만 들이는 꼴이고 나중에 코드 유지 보수가 안됩니다.
이럴 때 필요한데 Partial<Type>입니다.
Partial<Type>은 Type을 모두 옵셔널로 만들어 줄 수 있는데요.
이제 우리코드에서 MyUserOptionals 을 지워도 됩니다.
type MyUserOptionals = Partial<MyUser>
위와 같이 작성하시면 됩니다.
VS Code의 인텔리센스가 Partial<Type>에 의해 생성된 MyUserOptionals 의 상세 설명을 보여 주고 있는데요.
Partial<Type>의 역할 그대로 모든 항목을 옵셔널로 만들어 주고 있네요.
이제 실행해 볼까요?
아까와 같이 별다른 에러 없이 잘 실행 되고 있습니다.
즉, Partial<Type>은 위와 같은 경우 사용하면 아주 좋은 Utility Type입니다.
2. Required<Type>
Required<Type> 은 타입의 옵셔널을 없애주는 기능을 합니다.
interface MyUser {
name: string;
id: string;
email?: string;
phone?: string;
}
MyUser에서 옵셔널 항목은 email과 phone 항목이 있습니다.
그런데 다음과 같이 하면 어떻게 될까요?
type RequiredMyUser = Required<MyUser>
바로 아래 그림처럼 됩니다.
email과 phone의 옵셔널 부분을 없애줬네요.
Requried<Type>은 이런 기능을 합니다.
3. Pick<Type, Keys>
세번째로 알아볼 유틸리티 타입은 Pick인데요.
간단히 예를 들어 설명하겠습니다.
위에서 MyUser 항목이 있는데요.
여기서 email과 name 부분을 발췌하는 항목을 만들고 싶을 때가 있습니다.
다음과 같이 하시면 됩니다.
type JustEmailAndName = Pick<MyUser, "email" | "name">
인텔리센스는 정말 똑똑하네요. Pick 할 때 항목을 넣을 수 있는 Keys를 친절하게 알려 주고 있네요.
우리의 새로운 타입인 JustEmailAndName은 email이 옵셔널이고 name 항목이 있는 새로운 타입이 되었네요.
4. Record<Keys, Types>
이번에 알아볼 유틸리티 타입은 좀 어려운 타입인데요.
바로 Record 타입입니다.
예를 들어 알아 볼까요?
const mapById = (users: MyUser[]): Record<string, MyUser> => {
return users.reduce((a, v) => {
return {
...a,
[v.id]: v,
}
}, {})
}
console.log(mapById([
{ id: "foo", name: "Mr, Foo" },
{ id: "baz", name: "Mr. Baz" }
]));
mapById 함수는 MyUser[] 배열을 보여주는 함수인데요.
이 함수의 리턴타입이 바로 Record입니다.
Record타입은 이렇듯 특정 타입과 키를 이용해서 레코드를 만들 때 편한데요.
실행 결과를 볼까요?
실행 결과를 보시면 mapById 함수에 의해 id에 따라 그 id에 해당되는 객체를 전부 보여 주고 있습니다.
5. Omit<Type, Keys>
위의 예에서 보면 id가 중복되었는데요.
이럴 때 쓸 수 있는 유틸리티 타입이 바로 Omit입니다.
위에서 알아 보았던 Pick의 정반대 역할을 합니다.
const mapById = (users: MyUser[]): Record<string, Omit<MyUser, "id">> => {
return users.reduce((a, v) => {
return {
...a,
[v.id]: v,
}
}, {})
}
실행결과를 볼까요?
별다르게 변한게 없습니다.
Omit은 타입을 지정해주는 역할을 하기 때문에 id 를 빼고 싶으면 코드에서 직접 빼야 합니다.
다음과 같이 하면 됩니다.
const mapById = (users: MyUser[]): Record<string, Omit<MyUser, "id">> => {
return users.reduce((a, v) => {
const { id, ...other } = v;
return {
...a,
[id]: other
}
}, {})
}
위와 같이 코드를 바꾸고 실행해 볼까요?
실행 결과를 보시면 id 항목이 빠졌습니다.
Record<string, Omit<MyUser, "id">> 라고 쓰여져 있어 너무 복잡한데요.
Omit 부분을 다른 타입으로 만들어 봅시다.
type UserWithoutID = Omit<MyUser, "id">;
const mapById = (users: MyUser[]): Record<string, UserWithoutID> => {
return users.reduce((a, v) => {
const { id, ...other } = v;
return {
...a,
[id]: other
}
}, {})
}
코드가 훨씬 보기 좋아졌죠?
마지막으로 보너스로 알아볼 기능이 있는데요.
만약 MyUser 타입에서 id 부분이 string이 아닌 number라면 어떻게 될까요?
그러면 Record<string,.........> 부분을 수작업으로 Record<number,.....> 처럼 매번 바꿔줘야 합니다.
이런 수고를 덜어줄 수 있는 방법이 있는데요.
const mapById = (users: MyUser[]): Record<MyUser["id"], UserWithoutID> => {
return users.reduce((a, v) => {
const { id, ...other } = v;
return {
...a,
[id]: other
}
}, {})
}
Record<MyUser["id"], ......> 라고 쓰면 됩니다.
이렇게 쓰시면 MyUser의 "id" 타입이 지정되게 됩니다.
이상 타입스크립트의 유틸리티 타입에 대해 알아 보았습니다.
'코딩 > Typescript' 카테고리의 다른 글
타입스크립트 enum, Literal types - 타입스크립트 TypeScript 강좌 11편 (1) | 2021.12.12 |
---|---|
타입스크립트 readonly Immutability - 타입스크립트 TypeScript 강좌 10편 (0) | 2021.12.12 |
타입스크립트 keyof 연산자 - 타입스크립트 TypeScript 강좌 8편 (0) | 2021.11.08 |
타입스크립트 제네릭 - 타입스크립트 TypeScript 강좌 7편 (0) | 2021.11.08 |
타입스크립트 튜플 - 타입스크립트 TypeScript 강좌 6편 (0) | 2021.11.02 |