Skip to content

유틸리티 타입

11.3.1. 기본 유틸리티 타입

TypeScript 내장 유틸리티 타입을 적극 활용하여 타입 중복을 방지합니다.

유틸리티 타입용도설명
Partial<T>수정 폼모든 속성을 선택적으로 변환
Required<T>필수 보장모든 속성을 필수로 변환
Pick<T, K>부분 선택특정 속성만 추출
Omit<T, K>속성 제외특정 속성을 제거
Record<K, V>맵 구조키-값 쌍의 객체 타입 정의
typescript
interface Product { id: number; name: string; price: number; description: string }

type UpdateProductRequest = Partial<Omit<Product, 'id'>>
type ProductSummary = Pick<Product, 'id' | 'name' | 'price'>
type CategoryCount = Record<string, number>
  • 동일 타입에서 파생되는 변형은 유틸리티 타입으로 생성합니다. 속성을 수동으로 복사하지 않습니다.

11.3.2. API 응답 타입 정의 패턴

API 응답은 제네릭 래퍼 타입으로 일관된 구조를 유지합니다.

typescript
// src/types/common.ts
interface ApiResponse<T> {
  success: boolean
  data: T
  message: string
}

interface PaginatedResponse<T> {
  items: T[]
  page: number
  pageSize: number
  totalItems: number
  totalPages: number
}

interface ApiError {
  code: string
  message: string
  details?: Record<string, string[]>
}

// 활용 예시
async function fetchOrders(page: number): Promise<ApiResponse<PaginatedResponse<OrderDto>>> {
  const response = await api.get('/orders', { params: { page } })
  return response.data
}
  • 모든 API 응답 함수에 반환 타입을 명시합니다. 타입 추론에 의존하지 않습니다.

11.3.3. 제네릭 컴포넌트

공통 컴포넌트는 제네릭을 활용하여 타입 안전성을 유지합니다.

vue
<script setup lang="ts" generic="T extends { id: number; label: string }">
defineProps<{ options: T[]; modelValue: T | null }>()
const emit = defineEmits<{ 'update:modelValue': [value: T] }>()
</script>

제네릭 Composable은 다양한 타입에 대해 동일한 로직을 재사용합니다.

typescript
// src/composables/useAsyncData.ts
import { ref, type Ref } from 'vue'

export function useAsyncData<T>(fetcher: () => Promise<T>) {
  const data = ref<T | null>(null) as Ref<T | null>
  const loading = ref(false)

  async function execute() {
    loading.value = true
    try { data.value = await fetcher() }
    finally { loading.value = false }
  }

  return { data, loading, execute }
}

11.3.4. Readonly와 불변성

변경되어서는 안 되는 데이터에는 불변 타입을 적용합니다.

typescript
// Readonly — 객체 속성 변경 방지
const config: Readonly<{ apiBaseUrl: string; maxRetries: number }> = {
  apiBaseUrl: 'https://api.example.com',
  maxRetries: 3,
}

// ReadonlyArray — 배열 변경 방지
function calculateTotal(items: ReadonlyArray<{ price: number; qty: number }>): number {
  return items.reduce((sum, item) => sum + item.price * item.qty, 0)
}

// as const — 리터럴 타입 고정
const HTTP_STATUS = { OK: 200, NOT_FOUND: 404, SERVER_ERROR: 500 } as const
type HttpStatusCode = (typeof HTTP_STATUS)[keyof typeof HTTP_STATUS] // 200 | 404 | 500
  • 설정 객체, 상수 테이블에는 as const를 적용하여 리터럴 타입을 보존합니다.
  • 함수 파라미터로 배열을 받을 때 내부에서 변경하지 않는다면 ReadonlyArray<T>를 사용합니다.

TIENIPIA QUALIFIED STANDARD