타입스크립트 Utility Types로 반복 코드 줄이기

문제 상황

레거시 프로젝트에 TypeScript를 점진적으로 도입하는 중이었다. API 응답 타입을 정의하다 보니 비슷한 형태의 타입을 반복해서 작성하게 되었다.

interface User {
  id: number;
  email: string;
  name: string;
  createdAt: string;
}

interface UserCreateRequest {
  email: string;
  name: string;
}

interface UserUpdateRequest {
  email?: string;
  name?: string;
}

이런 식으로 CRUD마다 타입을 새로 정의하니 필드가 추가될 때마다 여러 곳을 수정해야 했다.

Utility Types 활용

TypeScript 내장 유틸리티 타입을 사용해 기본 타입에서 파생시키는 방식으로 변경했다.

interface User {
  id: number;
  email: string;
  name: string;
  createdAt: string;
}

type UserCreateRequest = Omit<User, 'id' | 'createdAt'>;
type UserUpdateRequest = Partial<UserCreateRequest>;
type UserResponse = Pick<User, 'id' | 'email' | 'name'>;

특히 OmitPick을 조합하니 의도가 명확해졌다. 생성 요청에는 id가 없어야 하고, 업데이트는 부분 수정이 가능하다는 것이 타입 정의만으로 드러났다.

커스텀 유틸리티 타입

프로젝트 전체에서 자주 쓰는 패턴은 별도 유틸리티로 만들었다.

type ApiResponse<T> = {
  data: T;
  error: string | null;
  timestamp: number;
};

type Nullable<T> = { [K in keyof T]: T[K] | null };

ApiResponse로 모든 API 응답을 래핑하니 에러 핸들링 로직이 일관되게 작성되었다.

결과

  • 타입 정의 코드량 약 30% 감소
  • User 인터페이스 수정 시 파생 타입 자동 반영
  • 새 팀원이 타입 구조 파악하는 시간 단축

유틸리티 타입은 TypeScript 3.5부터 대부분 안정화되었는데, 생각보다 활용도가 높았다. DRY 원칙이 타입 시스템에도 적용된다는 걸 체감했다.

타입스크립트 Utility Types로 반복 코드 줄이기