하루코딩 세싹이

[TypeScript] 실무에서 꼭 쓰는 유틸리티 타입 3종 - keyof, particial, Omit 본문

개발 준비/ReactJS&TypeScript

[TypeScript] 실무에서 꼭 쓰는 유틸리티 타입 3종 - keyof, particial, Omit

떼떼히 2025. 6. 5. 16:08
728x90
반응형

타입을 정의할 때 사용하는 keyof T

keyof T는 TypeScript에서 특정 타입 T의 모든 키 이름을 유니온 타입으로 만들어주는 유틸리티 타입이다. 예를 들어 type User = { name: string; age: number }일 때, keyof User는 'name' | 'age'가 된다. 이는 타입 수준에서만 존재하며, 코드 실행 중에는 사용할 수 없고 주로 제네릭 함수에서 타입 안전한 키 접근이나 매핑 타입을 만들 때 활용된다.

 

keyof T는 T 타입의 키들(속성 이름들)을 가져온다. [P in keyof T]는 그 키들 각각에 대해 순회하고, ?는 선택적 속성으로 만든다는 뜻이다.

type Partial<T> = {
  [P in keyof T]?: T[P];
};

 

keyof T는 TypeScript의 타입 수준 개념이라, 실행 시(console.log 등)에는 직접 사용할 수 없다. keyof는 컴파일 타임(작성할 때)에만 존재하고, 런타임(실행 시점)에는 사라지기 때문이다. 실제 객체에서 keyof처럼 속성 이름(key) 을 보고 싶을 때는 자바스크립트의 Object.keys()를 사용해야 한다.

 

Object.keys()는 실제 객체에서 key를 배열로 반환하므로 keyof T와 유사하게 동작하지만, 완전히 같지는 않다.

type User = {
  name: string;
  age: number;
};

const user: User = {
  name: "Alice",
  age: 25
};

// 이건 타입 수준에서만 존재함 (런타임에는 없음)
type UserKeys = keyof User; // 'name' | 'age'

// 런타임에서 key를 보고 싶으면 Object.keys 사용
console.log(Object.keys(user)); // ['name', 'age']

 

 

 

Particial<T> 이해하기

TypeScript의 유틸리티 타입(Utility Types)은 기존 타입을 변형하거나 조작하여 새로운 타입을 만드는 데 사용되는 내장 타입 도구이다. 코드의 재사용성을 높이고, 타입 선언을 간결하게 만들 수 있게 해준다. 그 중에서 partial<T>는 TypeScript에서 매우 자주 사용되는 유틸리티 타입 중 하나이다.

 

Partial<T>는 타입 T의 모든 속성을 선택적으로(optional) 만드는 타입이다. 즉, T가 갖고 있는 모든 프로퍼티에 ?가 붙은 것처럼 동작한다. Partial<T>는 객체의 일부만 수정하거나 업데이트할 때, 부분적인 정보만 있는 객체를 다룰 때, 초기화 시 기본값을 제외한 필드를 나중에 채울 때 사용한다.

type User = {
  name: string;
  age: number;
};

type PartialUser = Partial<User>;
// 위는 아래와 같음
// type PartialUser = {
//   name?: string;
//   age?: number;
// }

 

 

 

Particial<T> 사용하기

객체 일부만 업데이트 할 때

타입이 User이면 id, name, age 값을 다 받아야하지만, Particial<User>를 사용해서 일부 값만 받아 업데이트 할 수 있다. 아래의 예제는 age 값만 다시 받아 업데이트 한 예제이다.

type User = {
  id: number;
  name: string;
  age: number;
};

function updateUser(user: User, updates: Partial<User>): User {
  return { ...user, ...updates };
}

const oldUser: User = { id: 1, name: "Tom", age: 30 };

const updated = updateUser(oldUser, { age: 31 }); // name을 생략해도 OK

 

 

Redux Toolkit이나 Zustand에서 상태 일부만 갱신

type AppState = {
  isDarkMode: boolean;
  language: string;
  userName: string;
};

const updateState = (state: AppState, patch: Partial<AppState>): AppState => {
  return { ...state, ...patch };
};

const currentState: AppState = {
  isDarkMode: false,
  language: 'en',
  userName: 'John',
};

const newState = updateState(currentState, { language: 'ko' });

 


에러 메시지 처리용 타입 만들기
Partial<Record<keyof T, V>>는 폼 에러 메시지를 다룰 때 아주 유용한 패턴이다.

type FormFields = {
  username: string;
  password: string;
};

type ErrorMessages = Partial<Record<keyof FormFields, string>>;

// 결과 예시:
const errors: ErrorMessages = {
  username: "Username is required"
  // password는 생략 가능
};

 

 

DeepPartial로 중첩 객체까지 선택적으로 만들기

type UserProfile = {
  name: string;
  contact: {
    email: string;
    phone: string;
  };
};

type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};

const profile: DeepPartial<UserProfile> = {
  contact: {
    email: "test@example.com" // phone은 없어도 됨
  }
};



API 요청 시 DTO로 사용
Partial은 PUT보다는 PATCH 요청에서 많이 사용된다.

type UserRequest = {
  name: string;
  age: number;
  email: string;
};

// PATCH 요청: 일부 필드만 보낼 수 있음
const requestBody: Partial<UserRequest> = {
  age: 28
};

 

 

Omit<T, K> 이해하기

Omit<T, K>는 TypeScript에서 타입 T에서 특정 키 K를 제거한 새로운 타입을 생성하는 유틸리티 타입이다. 예를 들어 Omit<User, 'password'>는 User 타입에서 password 속성을 제외한 타입을 만들며, 보안 정보 제거, 수정 불가능한 필드 제외, 폼 데이터 정의 등 실무에서 자주 사용된다. K에는 제거할 키 이름 하나 또는 'a' | 'b' 형태의 여러 키를 지정할 수 있다.

기본 문법은 아래와 같으며 T에는 원본 타입을 입력하고, K에는 제거할 키 이름(string, union 등 가능)을 입력한다.

// 기본 문법
Omit<T, K>

// 원본 타입
type User = {
  id: number;
  name: string;
  email: string;
  password: string;
};

// password 속성을 제거한 새 타입
type PublicUser = Omit<User, 'password'>;

// 이제 PublicUser에는 password 속성이 없음
const user: PublicUser = {
  id: 1,
  name: 'Alice',
  email: 'alice@example.com'
};

 

 

아래의 예제를 보면 id, name, email로 이루어져 있는 User 타입에서 email을 제외한 UserWithoutEmail이라는 새로운 타입이 생성되는 걸 확인할 수 있다.

type User = {
  id: number;
  name: string;
  email: string;
};

type UserWithoutEmail = Omit<User, 'email'>;

/* 결과 타입:
type UserWithoutEmail = {
  id: number;
  name: string;
};
*/

 

 

Omit<T, K> 사용하기

여러 키 제거

type Admin = Omit<User, 'id' | 'email'>;

/* 결과 타입:
type Admin = {
  name: string;
};
*/



업데이트용 타입 만들기
데이터베이스의 ID나 생성일은 변경하지 않기 때문에, 업데이트 요청에서는 제외한 타입을 만드는 데 유용하다.

type Product = {
  id: number;
  name: string;
  price: number;
  createdAt: string;
};

type ProductUpdateRequest = Omit<Product, 'id' | 'createdAt'>;



React 컴포넌트에서 prop 제한
Link 컴포넌트는 type 속성이 필요 없으니 제거하고, href만 추가해서 사용한다.

type ButtonProps = {
  type: 'submit' | 'button';
  disabled: boolean;
  onClick: () => void;
};

type LinkProps = Omit<ButtonProps, 'type'> & {
  href: string;
};



반응형