유틸리티 타입
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>를 사용합니다.