본문 바로가기
Javascript

타입스크립트 - Utility Type & Mapped Type

by 안자바먹지 2021. 4. 13.
728x90

유틸리티 타입

 

이미 정의해 놓은 타입을 변환할 때 사용하기 좋은 문법이다. 꼭 이것을 사용하지 않더라도 인터페이스나 제네릭으로 타입을 변환할 수 있지만 유틸리티 타입을 사용함으로 인해 더 간결하게 정의할 수 있다. 

 

 

Pick

 

예를들어 어떠한 상품들의 목록이 있고, 그 상품의 상세 내용을 보는 페이지가 있다고 가정해 보자.

 

interface Product {
  id: number;
  name: string;
  price: number;
  brand: string;
  stock: number;
}

// 상품 목록 받아오는 API 함수
function fetchProducts(): Promise<Product[]> {
  //..
}

// Product의 일부 속성
interface ProductDetail {
  id: number;
  name: string;
  price: number;
}

// 상품의 상세내용 조회
function displayProductDetail(shoppingItem: ProductDetail) {
  //..
}

 

상품의 목록의 인터페이스 Product가 있고, 각 상품의 상세내용 인터페이스 ProductDetail이 있다. 상품의 상세내용을 조회하기 위해선 Product 인터페이스의 일부 속성이 필요하기 때문에 그것들을 사용하여 ProductDetail 인터페이스를 정의했다. 하지만 그만큼 중복코드가 생겨버린다. 

 

// 상품의 상세내용 조회
type ShoppingItem = Pick<Product, 'id' | 'name' | 'price'>;
function displayProductDetail(shoppingItem: ShoppingItem) {
  //..
}

 

이 때 사용하는 것이 바로 Pick 이다. Pick은 기존에 존재하는 타입에서 자신이 원하는 속성으로 별도의 타입을 만들수 있다. 

 

 

Omit

 

Omit은 Pick과 상반되는 기능을 한다고 볼 수 있다. 

Pick은 기존에 존재하는 타입에서 자신이 사용하고자 하는 속성만을 말 그대로 pick 해서 만드는 것이고, Omit 또한 말 그대로 기존에 존재하는 타입에서 지정된 속성만 제거한 타입을 정의한다.

 

interface Address {
  name: string;
  phone: number;
  address: string;
  company: string;
}

type Phone = Omit<Address, 'address'>;
const phoneBook: Phone = {
  name: '이름',
  phone: 1234,
  company: '회사',
};

type Food = Omit<Address, 'address' | 'company'>;
const kimbabHeaven: Food = {
  name: '김밥천국',
  phone: 5678,
};

 

 

Partial

 

Partial은 기존에 존재하는 타입의 부분 집합을 만족하는 타입을 정의할 수 있다.

위에서 설명했던 Product 코드에 이어서 설명하자면,

 

interface Product {
  id: number;
  name: string;
  price: number;
  brand: string;
  stock: number;
}

// ... 중략

interface UpdateProduct {
  id?: number;
  name?: string;
  price?: number;
  brand?: string;
  stock?: number;
}

// 상품 정보를 업데이트하는 함수
function updateProductItem(productItem: UpdateProduct) {}

 

어떠한 상품 정보를 업데이트하고자 할때 상황마다 업데이트 해야할 속성값들이 매번 다를 수 있으므로 모든 속성들에 대해 옵셔널 옵션( ? )을 줄 수도 있다.

 

하지만 이미 Product 인터페이스가 있음에도 불구하고 중복으로 UpdateProduct 인터페이스를 정의 해야 했다. 이 때 사용할 수 있는게 바로 Partial 이다. 

 

type UpdateProduct = Partial<Product>
function updateProductItem(productItem: UpdateProduct) {}

 

이렇게만 해주면 위에 옵셔널 속성을 사용한 UpdateProduct와 동일한 효과를 나타낸다. 

 

 

Partial 구현해보기

 

interface UserProfile {
  username: string;
  email: string;
  profilePhotoUrl: string;
}

// 첫 번째 축약방법
type UserProfileUpdate = {
  username?: UserProfile['username'];
  email?: UserProfile['email'];
  profilePhotoUrl?: UserProfile['profilePhotoUrl'];
};

// 두 번째 축약방법
// p 변수에 username, email, profilePhotoUrl이 각각 반복문을 돌며 할당된다.
type UserProfileUpdate = {
  [p in 'username' | 'email' | 'profilePhotoUrl']?: UserProfile[p];
};

// 세 번째 축약방법
// keyof를 사용하여 더 간단하게 줄일 수 있다.
type UserProfileUpdate = {
  [p in keyof UserProfile]?: UserProfile[p];
};

// 실제 partial의 구조
type Subset<T> = {
  [p in keyof T]?: T[p];
};

 

 

Mapped Type

 

맵드 타입이란 기존에 정의되어 있는 타입을 새로운 타입으로 변환해주는 문법이다. (자바스크립트 es6의 map 함수와 비슷)

 

type Foods = 'ForkCutlet' | 'Hamburger' | 'Pizza'
type FoodPrices = { [K in Foods]: number }

 

이렇게 할 경우 FoodsPrices 타입은 아래와 같이 된다.

 

type FoodPrices = {
  ForkCutlet: number;
  Hamburger: number;
  Pizza: number;
}

 

 

728x90

댓글