TypeScript 중첩 객체 타입 부분 수정 — 바깥에서 K 를 Omit 으로 빼고, 같은 키를 매핑 타입으로 다시 붙이면서 안쪽만 바꾼다.1

interface O {
  actions: { a: string; b: number }
}

// 중첩 속성 Optional로 변경
type MakeNestedOptional<T, K extends keyof T, OK extends keyof T[K]> = Omit<
  T,
  K
> & {
  [P in K]: Omit<T[K], OK> & Partial<Pick<T[K], OK>>
}
type Result = MakeNestedOptional<O, 'actions', 'b'> // { actions: { a: string; b?: number } }

// 중첩 속성 타입 오버라이드
type OverrideNested<T, K extends keyof T, Override> = Omit<T, K> & {
  [P in K]: Omit<T[K], keyof Override> & Override
}
type Result2 = OverrideNested<O, 'actions', { b: boolean }> // { actions: { a: string; b: boolean } }

한두 군데만 쓸 거면 그냥 손으로 타입 작성이 더 명확함. 유틸리티 타입은 반복 사용할 때만 가치.

// 라이브러리 객체 변이 주의
// ❌ info.actions.onDownload = undefined
// ✅ const modifiedInfo = { ...info, actions: { ...info.actions, onDownload: undefined } }

Footnotes

  1. Utility Types — Omit<Type, Keys> | TypeScript: “Constructs a type by picking all properties from Type and then removing Keys (string literal or union of string literals). The opposite of Pick.” ↩

`#499`